<?php
// +----------------------------------------------------------------------
// | 计划服务服务层
// +----------------------------------------------------------------------
// | 主要服务于计划数据处理
// +----------------------------------------------------------------------
// | 错误编码头 243xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\exception\LogicException;
use common\model\CosMediaModel;
use common\model\EntityModel;
use common\model\ModuleModel;
use common\model\OriginalPlanNodeModel;
use common\model\PlanModel;
use common\model\PlanTaskRelationModel;
use common\model\ProjectModel;
use common\model\ProjectTemplateModel;
use common\model\RecordModel;
use common\model\ReviewRequestModel;
use common\model\ReviewRequestNodeModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\TaskMemberModel;
use common\model\TaskMemberRelationModel;
use common\model\TaskModel;
use common\model\TaskRelationModel;
use common\model\TaskRelationTagModel;
use common\model\TimelogModel;
use common\model\UserCertCategoryModel;
use common\model\UserModel;
use common\model\VersionModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\Hook;
use think\module\Module;
use think\Tree;
use Throwable;
use Webman\Stomp\Client;

class PlanService
{
    /**
     * @var PlanModel
     */
    private $planModel;
    private $entityModel;
    private $taskModel;
    private $stepCategoryModel;
    private $taskMemberModel;
    private $projectModel;

    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->planModel = model(PlanModel::class);
        $this->entityModel = model(EntityModel::class);
        $this->taskModel = model(TaskModel::class);
        $this->stepCategoryModel = model(StepCategoryModel::class);
        $this->taskMemberModel = model(TaskMemberModel::class);
        $this->projectModel = model(ProjectModel::class);
    }

    /**
     * 给实体打组
     * @param $filter
     * @param $data
     * @return bool|int|string
     * @throws \Exception
     */
    public function createGroup($param)
    {
        $filter = $param['param']['filter'];
        $data = $param['data'] ?? null;

        if (!empty($data['group_uuid'])) {
            $isGroup = model(PlanModel::class)->where(['group_uuid' => $data['group_uuid']])->select();

            if (empty($isGroup)) {
                throw new LogicException("plan group not exist", ErrorCode::PLAN_GROUP_NOT_EXIST);
            }
            if (empty($data['group_name'])) {
                $data['group_name'] = $isGroup[0]['group_name'];
            }
        } else {
            $data['group_uuid'] = create_uuid();
            if (empty($data['group_name'])) {
                $data['group_name'] = '里程碑';
            }
        }

        if (!empty($data['start_time'])) {
            $data['start_time'] = strtotime($data['start_time']);
        }

        if (!empty($data['end_time'])) {
            $data['end_time'] = strtotime($data['end_time']);
        }

        $entityPlan = model(PlanModel::class)->where($filter)->select();
        $planEntityIds = array_column($entityPlan, 'entity_id');
        //筛选出未创建的
        $entity = model(EntityModel::class)->where(["id" => $filter['entity_id']])->select();
        $entityIdMapping = array_column($entity, null, 'id');

        //筛选出未保存的实体的计划
        $entityIdMapping = array_filter($entityIdMapping, function ($item) use ($planEntityIds) {
            return !in_array($item['id'], $planEntityIds);
        });

        $planData = [];
        if (!empty($entityIdMapping)) {
            $initialEpisodeCodes = array_column($entity, 'initial_episode_code');
            $episode = model(EntityModel::class)->where([
                "project_id" => $filter['project_id'],
                "code" => ['IN', join(',', $initialEpisodeCodes)],
            ])->find();

            $entityModuleMapping = array_column($entity, 'module_id');

            $stepCategory = model(StepCategoryModel::class)->where([
                "entity_id" => ['IN', join(',', $entityModuleMapping)],
                'parent_id' => 0,
            ])->select();

            $assetStepCategory = model(StepCategoryModel::class)->where(["code" => "asset"])->find();

            $stepCategoryIdMapping = array_column($stepCategory, 'id', 'entity_id');
            $assetStepCategoryPlan = model(PlanModel::class)->where([
                'step_category_id' => $assetStepCategory['id'],
                'entity_id' => $episode['id'],
                'type' => 'step_category'
            ])->find();

            if (empty($assetStepCategoryPlan)) {
                throw new LogicException("asset plan not exist", ErrorCode::ASSET_PLAN_NOT_EXIST);
            }

            foreach ($entityIdMapping as $entityId => $entity) {
                $planData[] = [
                    'start_time' => $assetStepCategoryPlan['end_time'],
                    'end_time' => $assetStepCategoryPlan['end_time'],
                    'step_category_id' => $stepCategoryIdMapping[$entity['module_id']],
                    'entity_id' => $entityId,
                    'project_id' => $filter['project_id'],
                    'type' => 'entity',
                    'uuid' => $data['group_uuid']
                ];
            }
        }


        $total = 0;
        if (!empty($planData)) { //添加未创建的计划
            model(PlanModel::class)->addAll($planData);
            $total = count($planData);
        }


        $total += model(PlanModel::class)->where($filter)->save($data);

        if (!empty($data['group_uuid'])) { //更新集中其他实体的时间或者名字
            $total = model(PlanModel::class)->where(['group_uuid' => $data['group_uuid']])->save($data);
        }

        return $total;
    }

    /**
     * 删除组 , 组内单个实体取消组
     * @param $filter
     * @return bool|int|string
     * @throws \Exception
     */
    public function deleteGroup($filter)
    {
        $data['group_uuid'] = '';
        $data['group_name'] = '';

        return model(PlanModel::class)->where($filter)->save($data);
    }

    /**
     * 设置组的时间
     * @param $filter
     * @param $data
     * @return bool|int|string
     * @throws \Exception
     */
    public function updateGroup($filter, $data)
    {
        if (!empty($data['start_time']) && !empty($data['end_time'])) {
            $data['start_time'] = strtotime($data['start_time']);
            $data['end_time'] = strtotime($data['end_time']);
        }

        return model(PlanModel::class)->where($filter)->save($data);
    }


    /**
     * 保存计划改动
     * @param $data
     * @return array|bool
     * @throws Exception
     */
    public function save($data)
    {
        $planModel = model(PlanModel::class);
        $planWhere = [
            'project_id' => $data['project_id'],
            'type' => $data['type']
        ];

        $checkData['start_time'] = strtotime($data['start_time']);
        $checkData['end_time'] = strtotime($data['end_time']);

        $exitId = 0;
        switch ($data["type"]) {
            case "entity": //此业务以没用
                if (!isset($data['step_category_id'])) {
                    throw new LogicException('plan step category id does not exist', ErrorCode::DATA_INVALID);
                }
                if (!isset($data['entity_id'])) {
                    throw new LogicException('plan entity id does not exist', ErrorCode::DATA_INVALID);
                }
                // 判断是否是资产还是概念, 如若拖动资产 判断是否存在概念的实体,判断是否计划中存在概念的计划 ,如果不存在判断是否晚于默认时间
                $planWhere['step_category_id'] = $data['step_category_id'];
                $stepCategory = model(StepCategoryModel::class)->find($data['step_category_id']);
                $mergeModuleConfig = OptionsService::getInstance()->getOptionsData('merge_module_config');

                //验证实体与父级的时间比较
                $moduleStepCategory = "";
                foreach ($mergeModuleConfig as $moduleName => $mergeModuleConfigItem) {
                    if (in_array($stepCategory['code'], explode(',', $mergeModuleConfigItem))) {
                        $moduleStepCategory = model(StepCategoryModel::class)->where(['code' => $moduleName])->find();
                    }
                }

                if (empty($moduleStepCategory)) {
                    throw new LogicException('category not configuration', ErrorCode::CATEGORY_NOT_CONFIGURATION);
                }

                $entity = model(EntityModel::class)->find($data['entity_id']);
                $episode = model(EntityModel::class)->where(['project_id' => $data['project_id'], 'code' => $entity['initial_episode_code']])->find();
                $assetPlan = model(PlanModel::class)
                    ->where(['step_category_id' => $moduleStepCategory['id'], 'project_id' => $data['project_id'], 'entity_id' => $episode['id'], 'type' => 'step_category'])
                    ->find();

                if ($checkData['start_time'] < $assetPlan['start_time']) {
                    throw new LogicException('entity plan not exceed step category plan start time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
                }

                if ($checkData['end_time'] > $assetPlan['end_time']) {
                    throw new LogicException('entity plan not exceed step category plan end time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
                }

                //验证资产与概念的时间比较,当计划为
                if ($stepCategory['code'] === 'asset') {
                    $asset = model(EntityModel::class)
                        ->field("JSON_UNQUOTE(JSON_EXTRACT(json,'$.\"{$stepCategory['code']}_design\"')) as {$stepCategory['code']}_design,initial_episode_code")->find($data['entity_id']);

                    if (!empty($asset["{$stepCategory['code']}_design"])) {
                        $designPlan = model(PlanModel::class)
                            ->where(['entity_id' => $asset["{$stepCategory['code']}_design"], 'project_id' => $data['project_id'], 'type' => 'entity'])->find();

                        if (empty($designPlan)) {
                            if ($checkData['start_time'] < $assetPlan['start_time']) {
                                throw new LogicException('asset cannot be less than design time', ErrorCode::ASSET_PLAN_TIME_NOT_LESS_THAN_DESIGN_TIME);
                            }
                        } elseif ($checkData['start_time'] < $designPlan['start_time']) {
                            throw new LogicException('asset cannot be less than design time', ErrorCode::ASSET_PLAN_TIME_NOT_LESS_THAN_DESIGN_TIME);
                        }
                    }
                } elseif ($stepCategory['code'] === 'design') { //需简化
                    $assetEntity = model(EntityModel::class)
                        ->where(["JSON_UNQUOTE(JSON_EXTRACT(json,'$.\"asset_design\"'))" => $data['entity_id']])->field('id,initial_episode_code')
                        ->find();

                    if (!empty($assetEntity)) {
                        $assetEntityPlan = model(PlanModel::class)->where(['entity_id' => $assetEntity['id'], 'project_id' => $data['project_id'], 'type' => 'entity'])->find();
                        if (empty($assetEntityPlan)) {
                            if ($checkData['start_time'] > $assetPlan['start_time']) {
                                throw new LogicException('design plan time not greater than asset time', ErrorCode::DESIGN_PLAN_TIME_NOT_GREATER_THAN_ASSET_TIME);
                            }
                        } elseif ($checkData['start_time'] > $assetEntityPlan['start_time']) {
                            throw new LogicException('design plan time not greater than asset time', ErrorCode::DESIGN_PLAN_TIME_NOT_GREATER_THAN_ASSET_TIME);
                        }
                    }
                }

                $planWhere['entity_id'] = $data['entity_id'];
                break;
            case "episode":
                if (!isset($data['entity_id'])) {
                    throw new LogicException('plan entity id does not exist', ErrorCode::DATA_INVALID);
                }
                return $this->episodePlanSave($data);
            case "step_category":
                if (!isset($data['step_category_id'])) {
                    throw new LogicException('plan step category id does not exist', ErrorCode::DATA_INVALID);
                }

                $planWhere['step_category_id'] = $data['step_category_id'];
                if (!empty($data['entity_id'])) {
                    $planWhere['entity_id'] = $data['entity_id'];
                    $episode = model(PlanModel::class)->where(['entity_id' => $data['entity_id'], 'type' => 'episode'])->find();

                    if ($checkData['start_time'] < $episode['start_time']) {
                        throw new LogicException('step category plan not exceed episode start time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
                    }

                    if ($checkData['end_time'] > $episode['end_time']) {
                        throw new LogicException('step category plan not exceed episode end time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
                    }
                }
                break;
            case "entity_step_category":  //此业务以没用
                if (!isset($data['step_category_id'])) {
                    throw new LogicException('plan step category id does not exist', ErrorCode::DATA_INVALID);
                }

                if (!isset($data['entity_id'])) {
                    throw new LogicException('plan entity id does not exist', ErrorCode::DATA_INVALID);
                }

                $entityPlan = model(PlanModel::class)
                    ->where([
                        'entity_id' => $data['entity_id'],
                        'type' => 'entity'
                    ])
                    ->find();

                if ($checkData['start_time'] < $entityPlan['start_time']) {
                    throw new LogicException('entity step category plan not exceed episode start time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
                }

                if ($checkData['end_time'] > $entityPlan['end_time']) {
                    throw new LogicException('entity step category plan not exceed episode end time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
                }

                $planWhere['step_category_id'] = $data['step_category_id'];
                $planWhere['entity_id'] = $data['entity_id'];
                break;
        }

        $exitId = $planModel->where($planWhere)->getField('id');


        if (!empty($exitId)) {
            $data['id'] = $exitId;
            $resData = $planModel->modifyItem($data);
            if (!$resData) {
                // 修改失败错误码 002
                throw new LogicException($planModel->getError(), ErrorCode::PLAN_MODIFY_FAILURE);
            } else {
                // 返回成功数据
                return $resData;
            }
        } else {
            // 执行新增操作
            $resData = $planModel->addItem($data);
            if (!$resData) {
                // 创建失败错误码 001
                throw new LogicException($planModel->getError(), ErrorCode::PLAN_ADD_FAILURE);
            } else {
                // 返回成功数据
                return $resData;
            }
        }
    }

    /**
     * 项目计划更新
     * @param $data
     * @throws \Exception
     */
    public function projectPlanSave($data)
    {
        $project = model(ProjectModel::class)->field('id,start_time,end_time')->find($data['id']);

        $newStartTime = isset($data['start_time']) ? strtotime($data['start_time']) : 0;
        $newEndTime = isset($data['end_time']) ? strtotime($data['end_time']) : 0;
        $projectId = $project['id'];
        $planType = ["step_category", "episode"];

        if (!empty($newStartTime) && !empty($newEndTime) && (($newEndTime - $newStartTime) === ($project['end_time'] - $project['start_time']))) {
            $panningTime = $newStartTime - $project['start_time'];
            if ($panningTime !== 0) {
                if ($panningTime > 0) {
                    $panningTime = "+" . $panningTime;
                }
                $projectSubItemPlan = model(PlanModel::class)->where(['project_id' => $projectId, 'type' => ['IN', ['step_category', 'episode']]])->field('id')->select();
                if (!empty($projectSubItemPlan)) {
                    $projectSubItemPlanIds = join(',', array_column($projectSubItemPlan, 'id'));
                    model(PlanModel::class)->execute("UPDATE plan SET start_time=start_time{$panningTime},end_time=end_time{$panningTime} WHERE id in ({$projectSubItemPlanIds})");
                }
            }
        } else {
            //集的时间更改

            //1.截取的情况  project_start_time > episode_start_time, project_start_time < episode_end_time 项目开始时间后移, 集的开始时间 只截取 项目计划时间开始
            //2.截取的情况  project_end_time < episode_end_time, project_end_time > episode_start_time 项目结束时间前移,集的结束 只截取 项目计划时间结束

            //3.截取的情况 project_start_time > episode_start_time, project_start_time >= episode_end_time 应当后移集 如果有超出计划结束时间,应当截取到计划结束时间
            //4.截取的情况 project_end_time < episode_end_time, project_end_time <= episode_start_time 应当前移集 如果有超出计划开始时间,应当截取到计划开始时间


            if (!empty($newStartTime)) {
                //获取超出部分 ,截取掉超出部分的数据
                // 项目的子项
                $endTime = $newEndTime;
                if (empty($endTime)) {
                    $endTime = $project['end_time'];
                }

                $this->projectSubItemTimeSave('start', $newStartTime, $project['start_time'], function () use ($projectId, $planType, $newStartTime, $endTime) {
                    $projectSubItemPlan = model(PlanModel::class)
                        ->where(['project_id' => $projectId, 'type' => ['IN', $planType], 'start_time' => ['LT', $newStartTime], 'end_time' => ['GT', $newStartTime]])->field('id')->select();
                    if (!empty($projectSubItemPlan)) {
                        $projectSubItemPlanIds = array_column($projectSubItemPlan, 'id');
                        model(PlanModel::class)->where(['id' => ['IN', $projectSubItemPlanIds]])->save(['start_time' => $newStartTime]);
                    }

                    if (!empty($endTime)) {
                        $threeProjectSubItemPlan = model(PlanModel::class)
                            ->where(['project_id' => $projectId, 'type' => ['IN', $planType], 'start_time' => ['LT', $newStartTime], 'end_time' => ['ELT', $newStartTime]])
                            ->field('id,start_time,end_time')->select();
                        if (!empty($threeProjectSubItemPlan)) {
                            // 获取超出计划完成时间的
                            $overEndTimePlanIds = [];
                            foreach ($threeProjectSubItemPlan as $threePlanItem) {
                                $threePlanTimeInterval = $threePlanItem['end_time'] - $threePlanItem['start_time'];
                                if ($threePlanTimeInterval > $endTime - $newStartTime) {
                                    $overEndTimePlanIds[] = ['id' => $threePlanItem['id'], 'start_time' => $newStartTime, 'end_time' => $endTime];
                                } else {
                                    $overEndTimePlanIds[] = ['id' => $threePlanItem['id'], 'end_time' => $newStartTime + $threePlanTimeInterval, 'start_time' => $newStartTime];
                                }
                            }
                            if (!empty($overEndTimePlanIds)) {
                                model(PlanModel::class)->saveAll($overEndTimePlanIds);
                            }
                        }
                    }
                });
            }

            if (!empty($newEndTime)) {

                $startTime = $newStartTime;
                if (empty($startTime)) {
                    $startTime = $project['start_time'];
                }


                $this->projectSubItemTimeSave('end', $newEndTime, $project['end_time'], function () use ($projectId, $planType, $newEndTime, $startTime) {
                    $projectSubItemPlan = model(PlanModel::class)
                        ->where(['project_id' => $projectId, 'type' => ['IN', $planType], 'end_time' => ['GT', $newEndTime], 'start_time' => ['LT', $newEndTime]])->field('id')->select();
                    if (!empty($projectSubItemPlan)) {
                        $projectSubItemPlanIds = array_column($projectSubItemPlan, 'id');
                        model(PlanModel::class)->where(['id' => ['IN', $projectSubItemPlanIds]])->save(['end_time' => $newEndTime]);
                    }


                    //第四种情况
                    if (!empty($startTime)) {
                        $fourProjectSubItemPlan = model(PlanModel::class)
                            ->where(['project_id' => $projectId, 'type' => ['IN', $planType], 'end_time' => ['GT', $newEndTime], 'start_time' => ['EGT', $newEndTime]])
                            ->field('id,start_time,end_time')->select();
                        if (!empty($fourProjectSubItemPlan)) {
                            // 获取超出计划完成时间的
                            $overStartTimePlanIds = [];
                            foreach ($fourProjectSubItemPlan as $fourPlanItem) {
                                $fourPlanTimeInterval = $fourPlanItem['end_time'] - $fourPlanItem['start_time'];
                                if ($fourPlanTimeInterval > $newEndTime - $startTime) {
                                    $overStartTimePlanIds[] = ['id' => $fourPlanItem['id'], 'start_time' => $startTime, 'end_time' => $newEndTime];
                                } else {
                                    $overStartTimePlanIds[] = ['id' => $fourPlanItem['id'], 'end_time' => $newEndTime, 'start_time' => $newEndTime - $fourPlanTimeInterval];
                                }
                            }
                            if (!empty($overStartTimePlanIds)) {
                                model(PlanModel::class)->saveAll($overStartTimePlanIds);
                            }
                        }
                    }

                });
            }
        }
    }

    /**
     * 项目子项计划时间更新
     * @param $timeType
     * @param $newTime
     * @param $oldTime
     * @param $dealFunction
     */
    public function projectSubItemTimeSave($timeType, $newTime, $oldTime, $dealFunction)
    {
        if (($timeType == 'start' && $newTime > $oldTime) || ($timeType == 'end' && $newTime < $oldTime)) {
            $dealFunction();
        }
    }


    /**
     * 集计划保存
     * @param $data
     * @return array|bool
     * @throws Exception
     */
    public function episodePlanSave($data)
    {
        $startTime = strtotime($data['start_time']);
        $endTime = strtotime($data['end_time']);
        $projectId = $data['project_id'];
        $type = $data['type'];


        $episode = model(EntityModel::class)->field('id,project_id')->find($data['entity_id']);
        if (empty($episode)) {
            return [];
        }
        $entityId = $episode['id'];
        $project = model(ProjectModel::class)->field('start_time,end_time')->find($episode['project_id']);
        if ($startTime < $project['start_time']) {
            throw new LogicException('episode plan not exceed project start time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
        }

        if ($endTime > $project['end_time']) {
            throw new LogicException('episode plan not exceed project end time', ErrorCode::PLAN_NOT_EXCEED_PARENT_PLAN_TIME);
        }

        //更新集下面的分类 计划开始时间 和计划完成时间

        $timeInterval = $endTime - $startTime;

        if ($timeInterval === 0) {
            return [];
        }

        $episodePlan = model(PlanModel::class)
            ->where(['project_id' => $projectId, 'type' => $type, 'entity_id' => $data['entity_id']])->field('id,start_time,end_time')->find();

        if (empty($episodePlan)) {
            $plan = model(PlanModel::class)->addItem($data);
        } else {
            $data['id'] = $episodePlan['id'];
            $episodePlanTimeInterval = $episodePlan['end_time'] - $episodePlan['start_time'];

            $planModel = model(PlanModel::class);
            $planModel->startTrans();
            try {
                $plan = $planModel->modifyItem($data);
                //判断是平移还是缩减 平移代表整体时间移动,平移则会让下面所有的进度平移. 缩减则是让超出的部分被截取掉
                if ($episodePlanTimeInterval == $timeInterval) {
                    $panningTime = $startTime - $episodePlan['start_time'];
                    if ($panningTime !== 0) {
                        if ($panningTime > 0) {
                            $panningTime = "+" . $panningTime;
                        }
                        $stepCategoryPlan = model(PlanModel::class)->where(['entity_id' => $entityId, 'type' => 'step_category'])->field('id')->select();
                        if (!empty($stepCategoryPlan)) {
                            $stepCategoryPlanIds = join(',', array_column($stepCategoryPlan, 'id'));
                            model(PlanModel::class)->execute("UPDATE plan SET start_time=start_time{$panningTime},end_time=end_time{$panningTime} WHERE id in ({$stepCategoryPlanIds})");
                        }
                    }
                } else {

                    //1.截取的情况  episode_start_time > step_category_start_time, episode_start_time < step_category_end_time 只截取 集计划时间开始
                    //2.截取的情况  episode_end_time < step_category_end_time, episode_end_time > step_category_start_time 只截取 集计划时间开始

                    //3.截取的情况 episode_start_time > step_category_start_time, episode_start_time > step_category_end_time 应当后移step_category 如果有超出计划结束时间,应当截取到计划结束时间
                    //4.截取的情况 episode_end_time < step_category_end_time, episode_end_time < step_category_start_time 应当前移step_category 如果有超出计划开始时间,应当截取到计划开始时间

                    //获取超出部分 ,截取掉超出部分的数据
                    $this->projectSubItemTimeSave('start', $startTime, $episodePlan['start_time'], function () use ($entityId, $startTime, $endTime) {
                        //第一种情况
                        $projectSubItemPlan = model(PlanModel::class)
                            ->where(['entity_id' => $entityId, 'type' => "step_category", 'start_time' => ['LT', $startTime], 'end_time' => ['GT', $startTime]])->field('id')->select();
                        if (!empty($projectSubItemPlan)) {
                            $projectSubItemPlanIds = array_column($projectSubItemPlan, 'id');
                            model(PlanModel::class)->where(['id' => ['IN', $projectSubItemPlanIds]])->save(['start_time' => $startTime]);
                        }

                        //第三种情况
                        $threeProjectSubItemPlan = model(PlanModel::class)
                            ->where(['entity_id' => $entityId, 'type' => "step_category", 'start_time' => ['LT', $startTime], 'end_time' => ['ELT', $startTime]])->field('id,start_time,end_time')->select();
                        if (!empty($threeProjectSubItemPlan)) {
                            // 获取超出计划完成时间的
                            $overEndTimePlanIds = [];
                            foreach ($threeProjectSubItemPlan as $threePlanItem) {
                                $threePlanTimeInterval = $threePlanItem['end_time'] - $threePlanItem['start_time'];
                                if ($threePlanTimeInterval > $endTime - $startTime) {
                                    $overEndTimePlanIds[] = ['id' => $threePlanItem['id'], 'start_time' => $startTime, 'end_time' => $endTime];
                                } else {
                                    $overEndTimePlanIds[] = ['id' => $threePlanItem['id'], 'end_time' => $startTime + $threePlanTimeInterval, 'start_time' => $startTime];
                                }
                            }
                            if (!empty($overEndTimePlanIds)) {
                                model(PlanModel::class)->saveAll($overEndTimePlanIds);
                            }
                        }
                    });

                    $this->projectSubItemTimeSave('end', $endTime, $episodePlan['end_time'], function () use ($entityId, $endTime, $startTime) {
                        //第二种情况
                        $twoProjectSubItemPlan = model(PlanModel::class)
                            ->where(['entity_id' => $entityId, 'type' => "step_category", 'end_time' => ['GT', $endTime], 'start_time' => ['LT', $endTime]])->field('id')->select();
                        if (!empty($twoProjectSubItemPlan)) {
                            $twoProjectSubItemPlanIds = array_column($twoProjectSubItemPlan, 'id');
                            model(PlanModel::class)->where(['id' => ['IN', $twoProjectSubItemPlanIds]])->save(['end_time' => $endTime]);
                        }

                        //第四种情况
                        $fourProjectSubItemPlan = model(PlanModel::class)
                            ->where(['entity_id' => $entityId, 'type' => "step_category", 'end_time' => ['GT', $endTime], 'start_time' => ['EGT', $endTime]])->field('id,start_time,end_time')->select();
                        if (!empty($fourProjectSubItemPlan)) {
                            // 获取超出计划完成时间的
                            $overStartTimePlanIds = [];
                            foreach ($fourProjectSubItemPlan as $fourPlanItem) {
                                $fourPlanTimeInterval = $fourPlanItem['end_time'] - $fourPlanItem['start_time'];
                                if ($fourPlanTimeInterval > $endTime - $startTime) {
                                    $overStartTimePlanIds[] = ['id' => $fourPlanItem['id'], 'start_time' => $startTime, 'end_time' => $endTime];
                                } else {
                                    $overStartTimePlanIds[] = ['id' => $fourPlanItem['id'], 'start_time' => $endTime - $fourPlanTimeInterval, 'end_time' => $endTime];
                                }
                            }
                            if (!empty($overStartTimePlanIds)) {
                                model(PlanModel::class)->saveAll($overStartTimePlanIds);
                            }
                        }
                    });


                }
                $planModel->commit();
            } catch (\Throwable $e) {
                $planModel->rollback();
                throw new LogicException($e->getMessage(), ErrorCode::PLAN_ADD_FAILURE);
            }
        }

        return $plan;
    }

    /**
     * 批量保存计划改动
     * @param $data
     * @return array
     * @throws Exception
     * @throws \Throwable
     */
    public function saveBatch($data)
    {
        $tasksData = $data['tasks'];
        $projectData = $data['project'];
        $planModel = model(PlanModel::class);
        $projectModel = model(ProjectModel::class);

        $planModel->startTrans();
        try {
            $projectData = [
                'id' => $projectData['id'],
//                'start_time' => $projectData['start_time'],
                'end_time' => $projectData['end_time'],
            ];
            $projectModel->modifyItem($projectData);
            $res = [];
            foreach ($tasksData as $item) {
                try {
                    $this->save($item);
                } catch (\Throwable $e) {
                    if ($e->getCode() == -243002) {
                        continue;
                    }
                    throw $e;
                }
            }
            $planModel->commit();
            return $res;
        } catch (\Throwable $e) {
            $planModel->rollback();
            throw $e;
        }
    }

    /**
     * 计算并保存子级计划时间
     * @param $parentPlan
     * @param $projectId
     * @return array|bool
     * @throws Exception
     */
    private function calculateAndSaveChildPlan($parentPlan, $projectId)
    {
        /**
         * 查找子级计划数据
         * 再次计算时间
         */
        $queryFields = [
            'step_category.id', 'step_category.code', 'step_category.name', 'step_category.index',
            'plan.id', 'plan.step_category_id', 'plan.start_time', 'plan.end_time', 'plan.project_id'
        ];
        $fields = build_complex_query_fields($queryFields, "__");


        $planData = model(PlanModel::class)
            ->join("step_category ON plan.step_category_id = step_category.id", "LEFT")
            ->where([
                'step_category.parent_id' => $parentPlan['step_category_id'],
                'plan.project_id' => $parentPlan['project_id'],
            ])
            ->field($fields)
            ->order("step_category.index")
            ->select();

        if (empty($planData)) {
            return true;
        }

        $stepCategoryIdData = array_column($planData, 'plan__step_category_id');

        foreach ($planData as &$planDatum) {
            $planDatum = separate_complex_query_data($planDatum, "__");
        }


        $categoryData = array_column($planData, 'step_category');
        $categoryData = array_column($categoryData, null, 'id');
        $plans = array_column($planData, 'plan');
        $plans = array_column($plans, null, 'step_category_id');


        $projectService = ProjectService::getInstance();
        $templateService = TemplateService::getInstance();

        $project = $projectService->getProject($projectId, 'project_template_id,start_time,end_time,delivery_time');
        $settings = $templateService->getConfigSetting(["id" => $project['project_template_id']], "step_category_plan");
        $tasks = $settings['tasks'] ?? [];

        $parentTask = array_filter($tasks, function ($item) use ($parentPlan) {
            return $item['step_category_id'] == $parentPlan['step_category_id'];
        });
        $parentTask = array_pop($parentTask);

        // 筛选task
        $tasks = array_filter($tasks, function ($item) use ($stepCategoryIdData) {
            return in_array($item['step_category_id'], $stepCategoryIdData);
        });

        $tasks = array_values($tasks);

        if (!isset($tasks[0]['step_category_index'])) {
            $tasks = array_map(function (&$item) use ($categoryData) {
                $item['step_category_index'] = $categoryData[$item['step_category_id']]['index'] ?? 0;
                return $item;
            }, $tasks);
        }

        $tasks = array_sort_by($tasks, 'step_category_index');

        $lastStartTime = $parentPlan['start_time'];
        $lastUnitLongest = 0;

        // 计算开始时间
        foreach ($tasks as $task) {
            if (!isset($categoryData[$task['id']])) {
                continue;
            }
            $plans[$task['step_category_id']]['start_time'] = $lastStartTime + $lastUnitLongest;
            $lastStartTime = $plans[$task['step_category_id']]['start_time'];
            $lastUnitLongest = $task['unit_longest_duration'];
        }

        // 计算结束时间
        $reverseTask = array_reverse($tasks);
        $lastEndTime = $parentPlan['end_time'];
        $lastUnitLongest = 0;
        foreach ($reverseTask as $task) {
            if (!isset($categoryData[$task['id']])) {
                continue;
            }
            // 如果时间不够 结束时间直接等于 开始时间
            if ($plans[$task['step_category_id']]['start_time'] >= ($lastEndTime - $lastUnitLongest)) {
                throw new LogicException("category plan duration not enough [{$parentTask['name']}]", ErrorCode::CATEGORY_PLAN_DURATION_NOT_ENOUGH);
            }
            $lastEndTime = $plans[$task['step_category_id']]['end_time'] = $lastEndTime - $lastUnitLongest;

            $lastUnitLongest = $task['unit_longest_duration'];
        }
        $res = [];
        foreach ($plans as $plan) {
            $res[] = model(PlanModel::class)->save($plan);
        }
        return $res;
    }

    /**
     * 项目集计划查询
     * @param $param
     * @return array
     */
    public function groupEpisode($param)
    {
        $projectId = $param['filter']['project_id'];
        $episodeModule = Module::$moduleDictData['module_index_by_code']['episode'];
        $entityModel = model(EntityModel::class);
        $episodes = $entityModel
            ->where([
                'project_id' => $projectId,
                'module_id' => $episodeModule['id'],
            ])
            ->field("id,name,code,start_time,end_time")
            ->order("code ASC")
            ->select();

        $planModel = model(PlanModel::class);
        $plans = $planModel->where([
            'project_id' => $projectId,
            'type' => 'episode'
        ])->select();
        $project = model(ProjectModel::class)->find($projectId);

        $plans = array_column($plans, null, 'entity_id');
        $episodes = array_map(function ($item) use ($plans, $project) {
            $item['row_type'] = "episode";
            if (isset($plans[$item['id']])) {
                $item['start_time'] = $plans[$item['id']]['start_time'];
                $item['end_time'] = $plans[$item['id']]['end_time'];
                $item['is_assign'] = 1; //以分配
            } else { //使用项目的默认时间
                $item['start_time'] = $project['start_time'];
                $item['end_time'] = $project['end_time'];
                $item['is_assign'] = 0; //未分配
            }
            StepService::formatGanttItem($item, "startnoearlierthan", "", false);
            return $item;
        }, $episodes);

        return StepService::formatStepCategoryPlanData($episodes, $project);
    }


    /**
     * 项目集计划查询
     * @param $param
     * @return array
     */
    public function getEpisodePlan($param)
    {
        $projectId = $param['filter']['project_id'];
        $episodeModule = Module::$moduleDictData['module_index_by_code']['episode'];
        $entityModel = model(EntityModel::class);
        $episodes = $entityModel
            ->where([
                'project_id' => $projectId,
                'module_id' => $episodeModule['id'],
            ])
            ->field("id,name,code,start_time,end_time")
            ->order("code ASC")
            ->select();

        $planModel = model(PlanModel::class);
        $plans = $planModel->where([
            'project_id' => $projectId,
            'type' => 'episode'
        ])->select();
        $project = model(ProjectModel::class)->find($projectId);

        $plans = array_column($plans, null, 'entity_id');

        $groupedEpisodeData = [];
        $episodesPlanData = [];
        foreach ($episodes as &$item) {
            $item['row_type'] = "episode";
            $currentPlan = $plans[$item['id']] ?? null;
            if (!empty($currentPlan)) {
                $item['start_time'] = $currentPlan['start_time'];
                $item['end_time'] = $currentPlan['end_time'];
                $item['is_assign'] = 1; //以分配
                if (!empty($currentPlan['group_uuid'])) {
                    StepService::formatGanttItem($item, "startnoearlierthan");
                    if (!array_key_exists($currentPlan['group_uuid'], $groupedEpisodeData)) {
                        $groupedEpisodeData[$currentPlan['group_uuid']] = [
                            'id' => $currentPlan['group_uuid'],
                            'name' => $currentPlan['group_name'],
                            'row_type' => 'group',
                            'children' => []
                        ];
                    }

                    $item['group_uuid'] = $currentPlan['group_uuid'];
                    $groupedEpisodeData[$currentPlan['group_uuid']]['children'][] = $item;
                    continue;
                }
            } else { //使用项目的默认时间
                $item['start_time'] = $item['end_time'] = $project['start_time'];
                $item['is_assign'] = 0; //未分配
            }
            StepService::formatGanttItem($item, "startnoearlierthan", "", false);
            $episodesPlanData[] = $item;

        }

        if (count($groupedEpisodeData)) {
            $episodesPlanData = array_merge(array_values($groupedEpisodeData), $episodesPlanData);
        }

//        $episodes = array_map(function ($item) use ($plans, $project) {
//
//            return $item;
//        }, $episodes);

        return StepService::formatStepCategoryPlanData($episodesPlanData, $project);
    }

    /**
     * 获得集工序分类排期
     * @param $param
     * @return array
     * @throws Exception
     */
    public function getEpisodeStepCategoryPlan($param)
    {
        $entityId = $param['filter']['entity_id'];
        $projectId = $param['filter']['project_id'];
//        $hideChild = $param['hide_child'] ?? 1;

        //从配置中筛掉只需要的分类
//        $optionService = new OptionsService();
//        $showPlanCategory = $optionService->getOptionsConfigItemData('module_config', 'show_plan_category');

        $stepCategoryList = model(StepCategoryModel::class)->where(['is_deprecate' => 'no', 'parent_id' => ['gt', 0]])->order("index")->select();

        $stepCategoryList = array_column($stepCategoryList, null, 'id');
        $planModel = model(PlanModel::class);

        $episodeStepCategoryPlan = $planModel->where(['entity_id' => $entityId, 'type' => 'step_category'])->select();
        $episodeStepCategoryPlan = array_column($episodeStepCategoryPlan, null, 'step_category_id');

        $episodePlan = $planModel->where(['project_id' => $projectId, 'entity_id' => $entityId, 'type' => 'episode'])->find();

        if (empty($episodePlan)) {
            throw new LogicException("episode_plan_not_exist", ErrorCode::EPISODE_PLAN_NOT_EXIST);
        }

//        $min = $episodePlan['start_time'];
//        $max = $episodePlan['end_time'];
        $stepCategoryList = array_map(function ($item) use ($episodePlan, $episodeStepCategoryPlan, $entityId, &$min, &$max) {
            $item['row_type'] = "step_category";
            $item['entity_id'] = $entityId;
            if (isset($episodeStepCategoryPlan[$item['id']])) {
                $item['start_time'] = $episodeStepCategoryPlan[$item['id']]['start_time'];
                $item['end_time'] = $episodeStepCategoryPlan[$item['id']]['end_time'];
                $item['is_assign'] = 1; //以分配
            } else {  //填补集的默认的时间
                $item['start_time'] = $item['end_time'] = $episodePlan['start_time'];
                $item['is_assign'] = 0; //未分配
            }

//            if ($item['start_time'] < $min || $min === 0) {
//                $min = $item['start_time'];
//            }
//            if ($item['end_time'] > $max || $max === 0) {
//                $max = $item['end_time'];
//            }
            StepService::formatGanttItem($item, "startnoearlierthan");
            return $item;
        }, $stepCategoryList);
        if (!empty($stepCategoryList)) {
            $stepCategoryList = array_values($stepCategoryList);
        }
//        $project = model(ProjectModel::class)->find($projectId);
//        $episodePlan['start_time'] = $project['start_time'];

//        $episodePlan['start_time'] = $min;
//        $episodePlan['end_time'] = $max;
        return StepService::formatStepCategoryPlanData($stepCategoryList, $episodePlan);
    }

    /**
     * 打组的查询 (暂时没用)
     * @param $code
     * @param $projectId
     * @param $episodeId
     * @param $params
     * @return array
     * @throws Exception
     */
    public function getAssetEntityPlan($code, $projectId, $episodeId, $params)
    {
        $stepCategoryModel = model(StepCategoryModel::class);
        $planModel = model(PlanModel::class);
        $entityModel = model(EntityModel::class);

        $gradeSearch = $params['filter']['grade'] ?? [];
        $moduleSearchList = $params['filter']['module_code'] ?? [];
        $categoryNameSearch = $params['filter']['category_name'] ?? [];
        $entityNameSearch = $params['filter']['entity_name'] ?? [];

        //获取集
        $entityEpisode = $entityModel->find($episodeId);
        $entityFilter = [
            'project_id' => $projectId,
            'initial_episode_code' => $entityEpisode['code'],
        ];

        if (!empty($gradeSearch)) {
            $entityFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_grade')))"] = $gradeSearch;
        }
        if (!empty($categoryNameSearch)) {
            $entityFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, CONCAT('$.',module.code,'_category')))"] = $categoryNameSearch;
        }

        if (!empty($entityNameSearch)) {
            $entityFilter['entity.name'] = $entityNameSearch;
        }

        $optionService = OptionsService::getInstance();
        $assetPlanCategory = $optionService->getOptionsConfigItemData('merge_module_config', $code);
        $assetPlanCategory = explode(',', $assetPlanCategory);
        if (empty($assetPlanCategory)) {
            throw new LogicException("options merge module config does not exist", ErrorCode::OPTIONS_MERGE_MODULE_CONFIG_DOES_NOT_EXIST);
        }

        if (!empty($moduleSearchList)) {

            $assetPlanCategory = array_filter($assetPlanCategory, function ($item) use ($moduleSearchList) {
                return in_array($item, $moduleSearchList);
            });
        }


        //获取分类名字
        //获取上级资产的分类的计划
        $assetStepCategory = $stepCategoryModel->where(['code' => $code])->find();
        $assetStepCategoryPlan = $planModel->where([
            'project_id' => $projectId,
            'entity_id' => $episodeId,
            'step_category_id' => $assetStepCategory['id'],
            'type' => 'step_category'
        ])->find();

        if (empty($assetStepCategoryPlan)) {
            throw new LogicException("asset plan not exist", ErrorCode::ASSET_PLAN_NOT_EXIST);
        }

        $stepCategoryList = $stepCategoryModel->where(
            [
                'code' => ['IN', join(',', $assetPlanCategory)]
            ]
        )->field('id,name,entity_id')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'entity_id');

        $episodeModuleMapping = [];

        foreach ($assetPlanCategory as $assetPlanCategoryItem) {
            $moduleId = Module::$moduleDictData['module_index_by_code'][$assetPlanCategoryItem]['id'];
            $episodeModuleMapping[$moduleId] = $assetPlanCategoryItem;
        }

        $episodeModuleIds = array_keys($episodeModuleMapping);
        $entityFilter['module_id'] = ['IN', $episodeModuleIds];

        //关卡、资产、概念

        $entityList = $entityModel
            ->join('module ON module.id = entity.module_id')
            ->where($entityFilter)->field('entity.id,entity.name,module_id,json')->select();

        $entityList = array_column($entityList, null, 'id');
        $entityIds = array_column($entityList, 'id');

        //获取所有实体的计划
        $entityPlans = $planModel->where([
            'project_id' => $projectId,
            'entity_id' => ["IN", join(',', $entityIds)],
            'type' => 'entity'
        ])->select();

        $entityPlans = array_column($entityPlans, null, 'entity_id');

        $entityData = [];
        $entityMapping = [];
        foreach ($entityList as $entityListItem) {
            $moduleCategory = $episodeModuleMapping[$entityListItem['module_id']] . '_category';
            $moduleGrade = $episodeModuleMapping[$entityListItem['module_id']] . '_grade';
            $entityListItemName = $entityListItem['name'];
            $entityListItemJson = json_decode($entityListItem['json'], true);
            $entityCategory = $entityListItemJson[$moduleCategory] ?? ""; //获取实体的分类
            $entityGrade = $entityListItemJson[$moduleGrade] ?? ""; //获取实体的等级

            if (!empty($entityGrade) && !empty($entityCategory)) {
                $key = md5("{$entityGrade}_{$entityCategory}_{$entityListItemName}");
                if (!array_key_exists($key, $entityMapping)) {
                    $entityMapping[$key] = [$entityListItem['id']];
                } else {
                    $entityMapping[$key][] = $entityListItem['id'];
                }
            }
        }
        $groupedEntityData = [];
        foreach ($entityMapping as $entityMappingItems) {
            foreach ($entityMappingItems as $entityMappingItem) {
                $entityListItem = $entityList[$entityMappingItem];
                $moduleCategory = $episodeModuleMapping[$entityListItem['module_id']] . '_category';
                $moduleGrade = $episodeModuleMapping[$entityListItem['module_id']] . '_grade';
                $entityListItemJson = json_decode($entityListItem['json'], true);
                $entityCategory = $entityListItemJson[$moduleCategory] ?? ""; //获取实体的分类
                $entityGrade = $entityListItemJson[$moduleGrade] ?? ""; //获取实体的等级
                $entityListItem['step_category_id'] = $stepCategoryList[$entityList[$entityMappingItem]['module_id']]['id']; //填补工序分类id
                $entityListItem['step_category_name'] = $stepCategoryList[$entityList[$entityMappingItem]['module_id']]['name']; //填补工序分类name
                $entityListItem['category'] = $entityCategory;
                $entityListItem['grade'] = $entityGrade;
                $entityListItem['row_type'] = "entity";
                unset($entityListItem['json']);
                $currentEntityPlan = $entityPlans[$entityListItem['id']] ?? null;
                if (!empty($currentEntityPlan)) {
                    $entityListItem['start_time'] = $currentEntityPlan['start_time'];
                    $entityListItem['end_time'] = $currentEntityPlan['end_time'];
                    $entityListItem['is_assign'] = 1; //以分配
                    if (!empty($currentEntityPlan['group_uuid'])) {
                        StepService::formatGanttItem($entityListItem, "startnoearlierthan");
                        if (!array_key_exists($currentEntityPlan['group_uuid'], $groupedEntityData)) {
                            $groupedEntityData[$currentEntityPlan['group_uuid']] = [
                                'id' => $currentEntityPlan['group_uuid'],
                                'name' => $currentEntityPlan['group_name'],
                                'row_type' => 'group',
                                'children' => []
                            ];
                        }

                        $entityListItem['group_uuid'] = $currentEntityPlan['group_uuid'];
                        $groupedEntityData[$currentEntityPlan['group_uuid']]['children'][] = $entityListItem;
                        continue;
                    }
                } else { //使用项目的默认时间
                    $entityListItem['start_time'] = $assetStepCategoryPlan['start_time'];
                    $entityListItem['end_time'] = $assetStepCategoryPlan['start_time'];
                    $entityListItem['is_assign'] = 0; //未分配
                }
                StepService::formatGanttItem($entityListItem, "startnoearlierthan");
                $entityData[] = $entityListItem;
            }
        }
        if (count($groupedEntityData)) {
            $entityData = array_merge(array_values($groupedEntityData), $entityData);
        }
//        $project = model(ProjectModel::class)->find($projectId);
//        $assetStepCategoryPlan['start_time'] = $project['start_time'];

        return StepService::formatStepCategoryPlanData($entityData, $assetStepCategoryPlan);
    }

    /**
     * 查询镜头计划 (暂时没用)
     * @param $code
     * @param $projectId
     * @param $episodeId
     * @param $tenantId
     * @return array
     * @throws Exception
     */
    public function getShotEntityPlan($code, $projectId, $episodeId, $tenantId)
    {
        /**
         * 查询序列
         * 查询镜头
         * 组装树 场=>序列=>镜头
         */

        $shotStepCategory = model(StepCategoryModel::class)->where(['code' => $code])->find();
        $shotPlan = model(PlanModel::class)->where([
            'project_id' => $projectId,
            'entity_id' => $episodeId,
            'step_category_id' => $shotStepCategory['id'],
            'type' => 'step_category'
        ])->find();

        if (empty($shotPlan)) {
            throw new LogicException("shot plan not exist", ErrorCode::SHOT_PLAN_NOT_EXIST);
        }

        $sequenceNameSearch = $params['filter']['sequence_name'] ?? '';
        $shotNameSearch = $params['filter']['shot_name'] ?? '';
        $page = $params['page'] ?? [1, 500];
        $order = $params['order'] ?? 'name,id';
        $sequenceModuleId = Module::$moduleDictData['module_index_by_code']['sequence']['id'];
        $shotModuleId = Module::$moduleDictData['module_index_by_code']['shot']['id'];
        $res = [
            'total' => 0,
            'rows' => [],
        ];

        $entityFilter = [
            'tenant_id' => $tenantId,
            'project_id' => $projectId,
            'is_generate' => 'yes',
        ];
        $episode = model(EntityModel::class)
            ->field("id,name,code,module_id,start_time,end_time,duration")
            ->find($episodeId);
        $entityFilter['initial_episode_code'] = $episode['code'];
        if (!empty($entityNameSearch)) {
            $entityFilter['name'] = $entityNameSearch;
        }
        if (!empty($sequenceNameSearch)) {
            $entityFilter['name'] = $sequenceNameSearch;
        }
        $entityQueryFields = ['id', 'name', 'code', 'duration', 'initial_episode_code', 'showings_number', 'module_id', 'entity_id', 'entity_module_id', 'json', 'is_assignee', 'initial_episode_code', 'plan_start_time', 'plan_end_time'];
        if (!empty($episode)) {
            $entityFilter['initial_episode_code'] = $episode['code'];
        }
        $sequenceFilter = $shotFilter = $entityFilter;
        $sequenceFilter['module_id'] = $sequenceModuleId;
        $shotFilter['module_id'] = $shotModuleId;
        if (!empty($sequenceNameSearch)) {
            $sequenceFilter['name'] = $sequenceNameSearch;
        }
        if (!empty($shotNameSearch)) {
            $shotFilter['name'] = $shotNameSearch;
        }
        $res['total'] = $this->entityModel->where($sequenceFilter)->count();

        // 先查询所有的实体 然后处理任务数据
        //序列处理
        $sequenceList = $this->entityModel
            ->where($sequenceFilter)
            ->page($page[0], $page[1])
            ->field(implode(',', $entityQueryFields) . ",'sequence' as type")
            ->order($order)
            ->select();

        $stepCategoryList = model(StepCategoryModel::class)->where([
            'entity_id' => ['IN', join(',', [$sequenceModuleId, $shotModuleId])],
            'paren_id' => 0,
        ])->field('id,name,entity_id')->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'entity_id');

        $taskData = [];
        if ($res['total'] > 0) {
            $sequenceIdList = array_column($sequenceList, 'id');

            $sequencePlanList = model(PlanModel::class)->where([
                'project_id' => $projectId,
                'entity_id' => ["IN", join(',', $sequenceIdList)],
                'type' => 'entity'
            ])->select();

            $sequencePlanList = array_column($sequencePlanList, null, 'entity_id');

            $sequenceList = array_map(function ($item) use ($sequencePlanList, $shotPlan, $stepCategoryList) {
                $item['row_type'] = "entity";
                $item['step_category_id'] = $stepCategoryList[$item['module_id']]['id'];
                if (isset($sequencePlanList[$item['id']])) {
                    $item['start_time'] = $sequencePlanList[$item['id']]['start_time'];
                    $item['end_time'] = $sequencePlanList[$item['id']]['end_time'];
                } else {
                    $item['start_time'] = $shotPlan['start_time'];
                    $item['end_time'] = $shotPlan['end_time'];
                }
                StepService::formatGanttItem($item, "startnoearlierthan");
                return $item;
            }, $sequenceList);


            //镜头处理
            $shotFilter['entity_id'] = ['IN', implode(',', $sequenceIdList)];
            $shotList = $this->entityModel
                ->where($shotFilter)
                ->field(implode(',', $entityQueryFields) . ",'shot' as type")
                ->order($order)
                ->select();

            $shotIdList = array_column($shotList, 'id');

            $shotPlanList = model(PlanModel::class)->where([
                'project_id' => $projectId,
                'entity_id' => ["IN", join(',', $shotIdList)],
                'type' => 'entity'
            ])->select();

            $shotPlanList = array_column($shotPlanList, null, 'entity_id');

            $shotList = array_map(function ($item) use ($shotPlanList, $shotPlan, $stepCategoryList) {
                $item['row_type'] = "entity";
                $item['step_category_id'] = $stepCategoryList[$item['module_id']]['id'];
                if (isset($shotPlanList[$item['id']])) {
                    $item['start_time'] = $shotPlanList[$item['id']]['start_time'];
                    $item['end_time'] = $shotPlanList[$item['id']]['end_time'];
                } else {
                    $item['start_time'] = $shotPlan['start_time'];
                    $item['end_time'] = $shotPlan['end_time'];
                }
                StepService::formatGanttItem($item, "startnoearlierthan");
                return $item;
            }, $shotList);

            $sequenceListGrouped = array_group_by($sequenceList, 'showings_number');
            ksort($sequenceListGrouped);
            $shotListGrouped = array_group_by($shotList, 'entity_id');

            foreach ($sequenceListGrouped as $scName => $sequenceList) {
                foreach ($sequenceList as &$sequence) {
                    $sequenceId = $sequence['id'];
                    $sequence = array_merge($sequence, json_decode($sequence['json'], true));
                    unset($sequence['json']);
                    $sequence['children'] = $tasksGrouped[$sequenceId] ?? [];
                    $shotList = $shotListGrouped[$sequenceId] ?? [];
                    foreach ($shotList as &$shot) {
                        $shot = array_merge($shot, json_decode($shot['json'], true));
                        unset($shot['json']);

                    }
                    $shotList = array_values($shotList);
                    $sequence['children'] = array_merge($sequence['children'], $shotList);
                }
                $taskData[] = [
                    'id' => $scName,
                    'name' => $scName,
                    'type' => 'showings_number',
                    'children' => $sequenceList,
                ];
            }
        }

//        $project = model(ProjectModel::class)->find($projectId);
//        $shotPlan['start_time'] = $project['start_time'];

        return StepService::formatStepCategoryPlanData($taskData, $shotPlan);
    }

    /**
     * 查找单个任务
     * @param $param
     * @return array|false|mixed|string|null
     */
    public function findPlan($param)
    {
        $planFilter['project_id'] = $param['filter']['project_id'];
        $planFilter['entity_id'] = $param['filter']['entity_id'];
        $planFilter['type'] = $param['filter']['type'];

        if (!empty($param['filter']['code'])) {
            $stepCategory = model(StepCategoryModel::class)->where(['code' => $param['filter']['code']])->find();
            $planFilter['step_category_id'] = $stepCategory['id'];
            $planFilter['type'] = 'step_category';
        }

        $plan = model(PlanModel::class)->where($planFilter)->find();
        return $plan;
    }


    /**
     * 查询集分类tree
     * @param $param
     * @return array|false|mixed|string
     * @throws Exception
     */
    public function getEpisodesStepCategoryTree($param)
    {
        $projectId = $param['filter']['project_id'];
        $hideChild = $param['hide_child'] ?? 1;

        $episodeModule = Module::$moduleDictData['module_index_by_code']['episode'];
        $entityModel = model(EntityModel::class);
        $episodes = $entityModel
            ->where([
                'project_id' => $projectId,
                'module_id' => $episodeModule['id'],
            ])
            ->field("id,name,code,start_time,end_time")
            ->order("code ASC")
            ->select();

        // 获得项目模板配置
        $stepCategoryModel = model(StepCategoryModel::class);
        $templateService = TemplateService::getInstance();
        $project = model(ProjectModel::class)->find($projectId);
        $categories = $stepCategoryModel
            ->field("id,code,parent_id,name,index,is_standard")
            ->order("index ASC")
            ->select();
        $categoryData = array_column($categories, null, 'id');
        $settings = $templateService->getConfigSetting(["id" => $project['project_template_id']], "step_category_plan");

        // 格式化数据
        $tasks = $settings['tasks'] ?? [];

        // 获取非弃用工序分类
        $noDepCategory = $stepCategoryModel->where(['is_deprecate' => 'no', 'parent_id' => ['gt', 0]])
            ->order("index")->select();
        $showPlanCategory = array_unique(array_column($noDepCategory, 'code'));

        if (!empty($showPlanCategory)) {
//            $showPlanCategory = explode(',', $showPlanCategory);
            $tasks = array_filter($tasks, function ($item) use (&$showPlanCategory, $categoryData) {
                return in_array($categoryData[$item['id']]['code'], $showPlanCategory);
            });
        }
        $tasks = array_filter($tasks, function ($item) use (&$categoryData) {
            return $categoryData[$item['step_category_id']]['is_standard'] == "yes";
        });
        $tasks = array_map(function ($item) use (&$categoryData) {
            return [
                "id" => $item['id'],
                "name" => $item['name'],
                "code" => $item['code'],
                "step_category_id" => $item['step_category_id'],
            ];
        }, $tasks);


        // 隐藏二级分类
        if ($hideChild == 1) {
            $tasks = array_filter($tasks, function ($item) use (&$categoryData) {
                return $categoryData[$item['step_category_id']]['parent_id'] == 0;
            });
        }

        // 外包项目 隐藏未收到的分类
//        if ($project['type'] == 'outsource') {
//            $moduleCodes = explode(',', $project['module_codes']);
//            $tasks = array_filter($tasks, function ($item) use ($moduleCodes) {
//                return in_array($item['code'], $moduleCodes);
//            });
//        }

        // 排序 按照工序分类 排好模板的顺序
        $tasks = array_map(function (&$item) use ($categoryData) {
            $item['step_category_index'] = $categoryData[$item['step_category_id']]['index'];
            return $item;
        }, $tasks);


        $tasks = array_sort_by($tasks, 'step_category_index');

        //  查询每一集的当前计划时间
        $plans = model(EntityModel::class)
            ->where([
                'project_id' => $projectId,
                'plan_start_time' => ['gt', 0],
                'plan_end_time' => ['gt', 0],
            ])
            ->group("initial_episode_code")
            ->field("min(plan_start_time) as start_time,max(plan_end_time) as end_time,initial_episode_code as episode_code")
            ->select();
        $plans = array_column($plans, null, 'episode_code');

        foreach ($episodes as &$episode) {
            if (isset($plans[$episode['code']])) {
                $episode['start_time'] = $plans[$episode['code']]['start_time'];
                $episode['end_time'] = $plans[$episode['code']]['end_time'];
            } else {
                $episode['start_time'] = $episode['end_time'] = 0;
            }

            $episode['real_id'] = $episode['id'];
            $episode['id'] = "episode_" . $episode['id'];
            $episode['set_time_flag'] = false;
            $episode['row_type'] = "episode";
            if ($episode['start_time'] > 0 && $episode['end_time'] > 0) {
                $episode['set_time_flag'] = true;
            }
            if ($episode['set_time_flag']) {
                $episode['children'] = array_map(function ($item) use ($episode) {
                    $item['real_id'] = $item['id'];
                    $item['id'] = $episode['id'] . "_category_" . $item['id'];
                    $item['row_type'] = "step_category";
                    return $item;
                }, array_values($tasks));
            }
        }

        return $episodes;
    }

    /**
     * 查询分类集tree
     * @param $param
     * @return array
     */
    public function getStepCategoryEpisodesTree($param): array
    {
        $projectId = $param['filter']['project_id'];
        $hideChild = $param['hide_child'] ?? 1;
        $showEpisode = $param['show_episode'] ?? 0;

        $episodeModule = Module::$moduleDictData['module_index_by_code']['episode'];
        $entityModel = model(EntityModel::class);
        $episodes = $entityModel
            ->where([
                'project_id' => $projectId,
                'module_id' => $episodeModule['id'],
            ])
            ->field("id,name,code,start_time,end_time")
            ->order("code ASC")
            ->select();

        // 获得项目模板配置
        $stepCategoryModel = model(StepCategoryModel::class);
        $templateService = TemplateService::getInstance();
        $project = model(ProjectModel::class)->find($projectId);
        $categories = $stepCategoryModel
            ->field("id,parent_id,name,index,is_standard")
            ->order("index ASC")
            ->select();
        $categoryData = array_column($categories, null, 'id');
        $settings = $templateService->getConfigSetting(["id" => $project['project_template_id']], "step_category_plan");

        // 格式化数据
        $tasks = $settings['tasks'] ?? [];

        $tasks = array_filter($tasks, function ($item) use (&$categoryData) {
            return $categoryData[$item['step_category_id']]['is_standard'] == "yes";
        });
        $tasks = array_map(function ($item) use (&$categoryData) {
            return [
                "id" => $item['id'],
                "name" => $item['name'],
                "code" => $item['code'],
                "step_category_id" => $item['step_category_id'],
            ];
        }, $tasks);


        // 隐藏二级分类
        if ($hideChild == 1) {
            $tasks = array_filter($tasks, function ($item) use (&$categoryData) {
                return $categoryData[$item['step_category_id']]['parent_id'] == 0;
            });
        }

        // 外包项目 隐藏未收到的分类
//        if ($project['type'] == 'outsource') {
//            $moduleCodes = explode(',', $project['module_codes']);
//            $tasks = array_filter($tasks, function ($item) use ($moduleCodes) {
//                return in_array($item['code'], $moduleCodes);
//            });
//        }

        // 排序 按照工序分类 排好模板的顺序
        $tasks = array_map(function (&$item) use ($categoryData) {
            $item['step_category_index'] = $categoryData[$item['step_category_id']]['index'];
            return $item;
        }, $tasks);
        $tasks = array_sort_by($tasks, 'step_category_index');


        // 查询集分类计划

        $plans = model(PlanModel::class)
            ->where(['project_id' => $projectId])
            ->group("entity_id,step_category_id")
            ->field("step_category_id,entity_id")
            ->select();

        $plansMap = array_group_by($plans, 'step_category_id');
        $plansMap = array_map(function ($plans) {
            return array_column($plans, 'entity_id');
        }, $plansMap);

        if (!empty($tasks)) {
            $tasks = array_values($tasks);
        } else {
            $tasks = [];
        }
        foreach ($tasks as &$task) {
            $task['real_id'] = $task['id'];
            $task['id'] = "step_category_" . $task['id'];
            $task['set_time_flag'] = true;
//            $task['step_category'] = "step_category";
            $task['row_type'] = "step_category";
            $episodeIdArray = $plansMap[$task['real_id']] ?? [];

            if ($showEpisode) {
                $task['children'] = array_map(function ($item) use ($task, &$episodeIdArray) {
                    $item['real_id'] = $item['id'];
                    $item['id'] = $task['id'] . "_episode_" . $item['id'];
                    $item['row_type'] = "episode";
                    $item['set_time_flag'] = true;

                    return $item;
                }, array_filter(array_values($episodes), function ($item) use (&$episodeIdArray) {
                    return in_array($item['id'], $episodeIdArray);
                }));
                $task['children'] = array_values($task['children']);
            }
        }


        return $tasks;
    }

    /**
     * 项目分类计划
     * @param $param
     * @return array
     * @throws Exception
     */
    public function getStepCategoryPlan($param): array
    {
        $projectId = $param['filter']['project_id'];
        $hideChild = $param['hide_child'] ?? 1;

        // 获得项目模板配置
        $stepCategoryModel = model(StepCategoryModel::class);
        $templateService = TemplateService::getInstance();
        $project = model(ProjectModel::class)->find($projectId);
        $categories = $stepCategoryModel
            ->field("id,parent_id,name,index,is_standard")
            ->order("index ASC")
            ->select();
        $categoryData = array_column($categories, null, 'id');
        $settings = $templateService->getConfigSetting(["id" => $project['project_template_id']], "step_category_plan");

        // 格式化数据
        $tasks = $settings['tasks'] ?? [];

        $tasks = array_filter($tasks, function ($item) use (&$categoryData) {
            return $categoryData[$item['step_category_id']]['is_standard'] == "yes";
        });
        $tasks = array_map(function ($item) use (&$categoryData) {
            return [
                "id" => $item['id'],
                "name" => $item['name'],
                "code" => $item['code'],
                "step_category_id" => $item['step_category_id'],
            ];
        }, $tasks);


        // 隐藏二级分类
        if ($hideChild == 1) {
            $tasks = array_filter($tasks, function ($item) use (&$categoryData) {
                return $categoryData[$item['step_category_id']]['parent_id'] == 0;
            });
        }

        // 外包项目 隐藏未收到的分类
//        if ($project['type'] == 'outsource') {
//            $moduleCodes = explode(',', $project['module_codes']);
//            $tasks = array_filter($tasks, function ($item) use ($moduleCodes) {
//                return in_array($item['code'], $moduleCodes);
//            });
//        }
        // 排序 按照工序分类 排好模板的顺序
        $tasks = array_map(function (&$item) use ($categoryData) {
            $item['step_category_index'] = $categoryData[$item['step_category_id']]['index'];
            return $item;
        }, $tasks);
        $tasks = array_sort_by($tasks, 'step_category_index');

        $stepCategoryIdArray = array_column($tasks, 'step_category_id');

        // 查询集分类计划

        $plans = model(PlanModel::class)
            ->where([
                'project_id' => $projectId,
                'type' => 'step_category',
                'step_category_id' => ['IN', implode(',', $stepCategoryIdArray)]
            ])
            ->count();
        if ($plans == 0) {
            $projectService = ProjectService::getInstance();
            $projectService->initProjectStepCategoryPlan($projectId);
        }
        $plans = model(PlanModel::class)
            ->join("step_category ON plan.step_category_id = step_category.id")
            ->where([
                'plan.project_id' => $projectId,
                'plan.type' => 'step_category',
                'step_category.id' => ['IN', implode(',', $stepCategoryIdArray)]
            ])
            ->field("plan.*,step_category.name,step_category.index as step_category_index")
            ->order("step_category.index ASC")
            ->select();

        $plansMap = array_group_by($plans, 'step_category_id');
        $stepCategoryPlan = [];
        array_map(function ($plans) use (&$stepCategoryPlan, &$project) {

            $startTime = null;
            $endTime = null;
            $id = 0;
            $name = "";
            $plans = array_sort_by($plans, 'step_category_index');
            foreach ($plans as $plan) {
                $id = $plan['step_category_id'];
                $name = $plan['name'];
                if ($plan['start_time'] < $startTime || $startTime == null) {
                    $startTime = $plan['start_time'];
                }
                if ($plan['end_time'] > $endTime || $endTime == null) {
                    $endTime = $plan['end_time'];
                }
            }
            $stepCategoryPlan[$id] = [
                'id' => "step_category_" . $id,
                'real_id' => $id,
                'row_type' => 'step_category',
                'name' => $name,
                'start_time' => $startTime,
                'end_time' => $endTime,
                'plan_start_time' => $project['start_time'],
                'plan_end_time' => $project['end_time'],
            ];
            StepService::formatGanttItem($stepCategoryPlan[$id], "startnoearlierthan");
        }, $plansMap);
        $projectData = model(ProjectModel::class)->where(['id' => $projectId])->find();

        $stepCategoryPlan = array_values($stepCategoryPlan);
        return StepService::formatStepCategoryPlanData($stepCategoryPlan, $projectData);

    }

    /**
     * 获得工序分类集排期
     * @param $param
     * @return mixed
     */
    public function getStepCategoryEpisodePlan($param): array
    {
        $stepCategoryId = $param['filter']['step_category_id'];
        $projectId = $param['filter']['project_id'];
        $hideChild = $param['hide_child'] ?? 1;

        $planModel = model(PlanModel::class);


        $where = [
            'plan.project_id' => $projectId, 'plan.step_category_id' => $stepCategoryId
        ];
        if ($hideChild) {
            $where['step_category.parent_id'] = 0;
        }
        $records = $planModel
            ->join("step_category ON step_category.id = plan.step_category_id", "LEFT")
            ->join("entity ON entity.id = plan.entity_id")
            ->field("plan.id,entity.name,step_category.code,step_category.id as step_category_id,step_category.parent_id,plan.start_time,plan.end_time")
            ->where($where)
            ->order("step_category.index ASC")
            ->select();

        $stepCategoryDate = [
            'start_time' => null,
            'end_time' => null
        ];
        $records = array_map(function ($item) use (&$stepCategoryDate) {

            if ($item['start_time'] < $stepCategoryDate['start_time'] || $stepCategoryDate['start_time'] == null) {
                $stepCategoryDate['start_time'] = $item['start_time'];
            }
            if ($item['end_time'] > $stepCategoryDate['end_time'] || $stepCategoryDate['end_time'] == null) {
                $stepCategoryDate['end_time'] = $item['end_time'];
            }

            $item['row_type'] = "step_category";
            StepService::formatGanttItem($item, "startnoearlierthan");
            return $item;
        }, $records);

        if (!empty($records)) {
            $tree = new Tree('step_category_id', 'parent_id');
            $tree->load($records);
            $records = $tree->DeepTree();
        }

        return StepService::formatStepCategoryPlanData($records, $stepCategoryDate);
    }

    /**
     * 产能计算
     * @param $param
     * @param $tenantId
     * @return array
     * @throws \Exception
     */
    public function calculateCapacity($param, $tenantId)
    {
        $episodeId = $param['filter']['episode_id'] ?? 0;
        $stepCategoryId = $param['filter']['step_category_id'] ?? 0;
        $projectId = $param['filter']['project_id'] ?? 0;

        $where = [
            "task.user_cert_id" => ['gt', 0],
            "task.step_type" => "step_category",
        ];
        if ($episodeId) {
            $episode = model(EntityModel::class)->find($episodeId);
            if (!empty($episode)) {
                $where['entity.project_id'] = $episode['project_id'];
                $where['entity.initial_episode_code'] = $episode['code'];
            }
        }

        if ($projectId) {
            $where['entity.project_id'] = $projectId;
        }

        if ($stepCategoryId) {

            $stepCategories = model(StepCategoryModel::class)->where(['parent_id' => $stepCategoryId])->field("id")->select();
            if (!empty($stepCategories)) {
                $where['task.step_category_id'] = ['IN', implode(',', array_column($stepCategories, 'id'))];
            } else {
                $where['task.step_category_id'] = ["eq", 0];
            }
        }

        $taskLevelCategoryCount = model(EntityModel::class)
            ->join("task ON task.entity_id = entity.id")
            ->where($where)
            ->group("task.capacity_mark,task.user_cert_id")
            ->field("task.capacity_mark,task.user_cert_id")
            ->select();

        $needCertsIdArray = array_column($taskLevelCategoryCount, 'user_cert_id', 'user_cert_id');
        $certs = model(UserCertCategoryModel::class)
            ->join("user_cert ON user_cert.user_cert_category_id = user_cert_category.id")
            ->field("user_cert.id,user_cert_category.name,user_cert.level,user_cert.level_category")
            ->select();

        $certsMap = array_column($certs, null, 'id');

        //获得所有部门成员
        $tenantMembers = teamones_request('teamones-im', 'tenant/select_department_user', ['param' => [
            'filter' => [
                'tenant_id' => $tenantId,
            ],
        ]])['data'];
        if ($tenantMembers['rows'] > 0) {
            $userService = UserService::getInstance();

            $userIdArray = array_column($tenantMembers['rows'], 'user_id');
            // saas用户
            $saasUsers = $userService->getUsersByUnionIds($userIdArray, 'id,user_cert_id');

            $saasUsersNeeded = array_filter($saasUsers, function ($user) use (&$needCertsIdArray) {
                return $user['user_cert_id'] > 0 && in_array($user['user_cert_id'], $needCertsIdArray);
            });
            $saasUsersNeededGrouped = array_group_by($saasUsersNeeded, 'user_cert_id');
            $certsUserMap = [];
            foreach ($saasUsersNeededGrouped as $saasUsers) {
                $certsUserMap[$saasUsers['0']['user_cert_id']] = count($saasUsers);
            }
        }

        $res = [];
        $levelCategoryCountGroupByUserCertId = array_group_by($taskLevelCategoryCount, 'user_cert_id');

        foreach ($taskLevelCategoryCount as $item) {
            $tmp = [
                'cert' => $certsMap[$item['user_cert_id']] ?? null,
                'want' => count($levelCategoryCountGroupByUserCertId[$item['user_cert_id']] ?? []),
                'inside_have' => $certsUserMap[$item['user_cert_id']] ?? 0,
                'need_fill_in' => 0,
            ];
            $tmp['need_fill_in'] = $tmp['inside_have'] > $tmp['want'] ? 0 : $tmp['want'] - $tmp['inside_have'];
            $res[] = $tmp;
        }

        return $res;
    }

    /**
     * 计划面板数据
     * @param $param
     * @param int $tenantId
     * @return array
     */
    public function planPanel($param, int $tenantId)
    {
        $moduleCode = $param['module_code'];
        $projectId = $param['project_id'];

        $stepCategory = model(StepCategoryModel::class)->where(['code' => $moduleCode])->find();

        $currentModule = Module::$moduleDictData['module_index_by_code'][$moduleCode];

        $plan = model(PlanModel::class)
            ->where(['project_id' => $projectId, 'step_category_id' => $stepCategory['id']])
            ->select();
        // 排过计划的实体
        $entitiesPlaned = model(EntityModel::class)
            ->where([
                'module_id' => $currentModule['id'],
                'project_id' => $projectId,
                'plan_start_time' => ['gt', 0],
                'plan_end_time' => ['gt', 0],
            ])
            ->field("id,name,code,plan_start_time,plan_end_time,is_assignee")
            ->order("initial_episode_code")
            ->select();

        foreach ($entitiesPlaned as &$task) {
            $task['start_time'] = $task['plan_start_time'];
            $task['end_time'] = $task['plan_end_time'];
            $task['resourceId'] = $stepCategory['id'];
            if ($task['is_assignee'] == "yes") {
                $task['draggable'] = false;
                $task['resizable'] = false;
            } else {
                $task['draggable'] = true;
                $task['resizable'] = true;
            }
            $task['resizable'] = $stepCategory['id'];
            StepService::formatGanttItem($task, null, 'event', false);
        }
        $stepCategory['role'] = $stepCategory['code'];
        $res = StepService::formatStepCategoryPlanData([], [$plan], [], [], $entitiesPlaned, $stepCategory);
        return $res;
    }

    /**
     * 未排期数据
     * @param $param
     * @param int $tenantId
     * @return array|false|mixed|string
     */
    public function unPlannedData($param, int $tenantId)
    {
        $moduleCode = $param['module_code'];
        $projectId = $param['project_id'];
        $currentModule = Module::$moduleDictData['module_index_by_code'][$moduleCode];

        // 排过计划的实体
        $entitiesPlaned = model(EntityModel::class)
            ->where([
                'module_id' => $currentModule['id'],
                'project_id' => $projectId,
                'plan_start_time' => ['eq', 0],
                'plan_end_time' => ['eq', 0],
            ])
            ->field("id,name,code,plan_start_time,plan_end_time,is_assignee")
            ->order("initial_episode_code")
            ->select();
        foreach ($entitiesPlaned as &$entity) {
            $entity['duration'] = number_floor_precision(($entity['plan_end_time'] - $entity['plan_start_time']) / (3600 * 24), 2);
            $entity['durationUnit'] = "day";
        }
        return $entitiesPlaned;
    }

    /**
     * 计划查询
     * @param array $filter
     * @return array|false|mixed|string|null
     */
    public function getPlan(array $filter, $fields = '*')
    {
        return $this->planModel->where($filter)->field($fields)->find();
    }

    /**
     * 计划查询
     * @param array $filter
     * @return array|false|mixed|string|null
     */
    public function getPlans(array $filter)
    {
        return $this->planModel->where($filter)->select();
    }

    /**
     * 查询镜头模块 序列分类计划
     * @param $code
     * @param $projectId
     * @param $episodeId
     * @param $tenantId
     * @param $params
     * @return array
     * @throws Exception
     */
    public function getShotEntityStepPlan($code, $projectId, $episodeId, $tenantId, $params)
    {
        /**
         * 查询序列
         * 查询分类信息
         * 组装计划信息
         */
        $shotStepCategory = model(StepCategoryModel::class)->where(['code' => $code])->find();
        $shotPlan = model(PlanModel::class)->where([
            'project_id' => $projectId,
            'entity_id' => $episodeId,
            'step_category_id' => $shotStepCategory['id'],
            'type' => 'step_category'
        ])->find();
        if (empty($shotPlan)) {
            throw new LogicException("shot plan not exist", ErrorCode::SHOT_PLAN_NOT_EXIST);
        }
        $sequenceNameSearch = $params['filter']['sequence_name'] ?? '';
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $order = $params['order'] ?? 'name,id';
        $sequenceModuleId = Module::$moduleDictData['module_index_by_code']['sequence']['id'];
        $shotModuleId = Module::$moduleDictData['module_index_by_code']['shot']['id'];
        $res = [
            'total' => 0,
            'rows' => [],
        ];
        $entityFilter = [
            'tenant_id' => $tenantId,
            'project_id' => $projectId,
            'is_generate' => 'yes',
        ];
        $episode = model(EntityModel::class)
            ->field("id,name,code,module_id,start_time,end_time,duration")
            ->find($episodeId);
        $entityFilter['initial_episode_code'] = $episode['code'];
        if (!empty($sequenceNameSearch)) {
            $entityFilter['name'] = $sequenceNameSearch;
        }
        $entityQueryFields = ['id', 'name', 'code', 'duration', 'initial_episode_code', 'showings_number', 'module_id', 'entity_id', 'entity_module_id', 'is_assignee', 'initial_episode_code', 'plan_start_time', 'plan_end_time'];
        if (!empty($episode)) {
            $entityFilter['initial_episode_code'] = $episode['code'];
        }
        $sequenceFilter = $shotFilter = $entityFilter;
        $sequenceFilter['module_id'] = $sequenceModuleId;
        if (!empty($params['filter']['showings_number'])) { //加入序列场次的筛选条件
            $sequenceFilter['showings_number'] = $params['filter']['showings_number'];
        }

        $shotFilter['module_id'] = $shotModuleId;
        if (!empty($sequenceNameSearch)) {
            $sequenceFilter['name'] = $sequenceNameSearch;
        }
        if (!empty($shotNameSearch)) {
            $shotFilter['name'] = $shotNameSearch;
        }
        $res['total'] = $this->entityModel->where($sequenceFilter)->count();
        // 分类信息
        $stepCategoryList = model(StepCategoryModel::class)
            ->where([
                'entity_id' => ['IN', "$sequenceModuleId,$shotModuleId"],
                'parent_id' => 0,
            ])
            ->field('id,name,code,entity_id')->select();
        // 二级分类id
        $childStepCategoryList = model(StepCategoryModel::class)
            ->where(['parent_id' => ['IN', implode(',', array_column($stepCategoryList, 'id'))]])
            ->field('id,name,code,entity_id')->select();
        //序列处理
        $sequenceList = $this->entityModel
            ->where($sequenceFilter)
            ->page($page[0], $page[1])
            ->field(implode(',', $entityQueryFields) . ",'entity' as type")
            ->order($order)
            ->select();
        $stepCategoryList = array_column($stepCategoryList, null, 'entity_id');

        if ($res['total'] > 0) {
            $sequenceIdList = array_column($sequenceList, 'id');
            $sequencePlanList = model(PlanModel::class)
                ->where([
                    'project_id' => $projectId,
                    'entity_id' => ["IN", join(',', $sequenceIdList)],
                    'type' => 'entity'
                ])->select();
            $sequencePlanList = array_column($sequencePlanList, null, 'entity_id');
            $sequenceStepCategoryPlanList = $this->planModel
                ->where([
                    'project_id' => $projectId,
                    'entity_id' => ["IN", join(',', $sequenceIdList)],
                    'step_category_id' => ['IN', array_column($childStepCategoryList, 'id')],
                    'type' => 'entity_step_category',
                ])
                ->select();
            $sequenceStepCategoryPlanList = array_group_by($sequenceStepCategoryPlanList, 'entity_id', 'step_category_id');

            // 镜头数量查询
            $shotNamesList = $this->entityModel->where(['entity_id' => ['IN', implode(',', $sequenceIdList)]])
                ->field('count(id) as shot_number,entity_id as sequence_id')
                ->group('entity_id')
                ->select();
            $shotNamesList = array_column($shotNamesList, 'shot_number', 'sequence_id');


            foreach ($sequenceList as &$sequence) {
                $sequenceId = $sequence['id'];
                $shotNumber = $shotNamesList[$sequenceId] ?? 0;
                $sequence ['name'] = $sequence['name'] . "(镜头数量: {$shotNumber})";
                $sequencePlan = $sequencePlanList[$sequenceId] ?? $shotPlan;
                $sequence['start_time'] = $sequencePlan['start_time'];
                $sequence['end_time'] = $sequencePlan['end_time'];
                $sequence['row_type'] = "entity";
                $sequence['step_category_id'] = $stepCategoryList[$sequence['module_id']]['id'] ?? 0;
                StepService::formatGanttItem($sequence, "startnoearlierthan");
                $sequence['children'] = [];
                foreach ($childStepCategoryList as $stepCategory) {
                    $currentPlan = $sequenceStepCategoryPlanList[$sequenceId][$stepCategory['id']] ?? ['start_time' => $sequence['start_time'], 'end_time' => $sequence['end_time']];
                    $sequenceChildrenPlan = [
                        'id' => "{$sequenceId}_{$stepCategory['id']}",
                        'step_category_id' => $stepCategory['id'],
                        'entity_id' => $sequenceId,
                        'name' => $stepCategory['name'],
                        'code' => $stepCategory['code'],
                        'start_time' => $currentPlan['start_time'],
                        'end_time' => $currentPlan['end_time'],
                        'row_type' => 'entity_step_category',
                    ];
                    StepService::formatGanttItem($sequenceChildrenPlan, "startnoearlierthan");
                    $sequence['children'][] = $sequenceChildrenPlan;
                }
            }
        }

        return StepService::formatStepCategoryPlanData($sequenceList, $shotPlan);
    }

    /**
     * 查询集的分类计划
     * @param int $projectId
     * @param int $stepCategoryId
     * @return array|false|mixed|string
     */
    public function getFixedEpisodeStepCategoryPlan(int $projectId, int $stepCategoryId)
    {
        $episodeList = model(EntityModel::class)->where(['project_id' => $projectId, 'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id']])->field('id,name,code')->order('name ASC')->select();
        $episodeIdList = array_column($episodeList, 'id');
        $stepCategoryPlanFilter = [
            'type' => 'step_category', 'project_id' => $projectId, 'entity_id' => ['IN', $episodeIdList], 'step_category_id' => $stepCategoryId
        ];
        $stepCategoryPlan = model(PlanModel::class)->where($stepCategoryPlanFilter)->select();
        $stepCategoryPlan = array_column($stepCategoryPlan, null, 'entity_id');
        $episodePlanFilter = [
            'type' => 'episode', 'project_id' => $projectId, 'entity_id' => ['IN', $episodeIdList],
        ];
        $episodePlan = model(PlanModel::class)->where($episodePlanFilter)->select();
        $episodePlan = array_column($episodePlan, null, 'entity_id');

        foreach ($episodeList as &$episode) {
            if (isset($stepCategoryPlan[$episode['id']])) {
                $episode['plan'] = $stepCategoryPlan[$episode['id']];
            } else if (isset($episodePlan[$episode['id']])) {
                $episode['plan'] = $episodePlan[$episode['id']];
            } else {
                $episode['plan'] = null;
            }
        }
        return $episodeList;
    }

    /**
     * 项目分类计划查询
     * @param int $projectId
     * @param $moduleCode
     * @return array|false|mixed|string
     */
    public function getProjectStepCategoryPlan(int $projectId, $moduleCode)
    {
        return model(StepCategoryModel::class)
            ->join("plan ON plan.step_category_id = step_category.id")
            ->where([
                'plan.project_id' => $projectId,
                'step_category.code' => $moduleCode,
            ])
            ->field("plan.*,step_category.code as step_category_code")
            ->select();
    }

    /**
     * 复制集计划
     * @param int $sourceProjectId
     * @param int $shadowProjectId
     * @param int $tenantId
     * @return bool|int|string
     * @throws Exception
     */
    public function copyEpisodePlan(int $sourceProjectId, int $shadowProjectId, int $tenantId)
    {

        $planModel = model(PlanModel::class);
        $sourcePlan = $planModel->where(['project_id' => $sourceProjectId])->field("id,uuid,project_id,tenant_id", true)->select();

        if (!empty($sourcePlan)) {
            $shadowProjectPlan = array_map(function ($item) use ($tenantId, $shadowProjectId) {
                $item['uuid'] = create_uuid();
                $item['tenant_id'] = $tenantId;
                $item['project_id'] = $shadowProjectId;
                return $item;
            }, $sourcePlan);
            $res = $planModel->addAll($shadowProjectPlan);
            if (!$res) {
                throw new LogicException($planModel->getError(), ErrorCode::PLAN_ADD_FAILURE);
            }
            return $res;
        }
        return 0;
    }

    /**
     * 获取项目计划列表
     * @param $param
     * @return array
     */
    public function getProjectPlanList($param)
    {
        $filter = $param["filter"];
        $stepCategoryId = $filter['step_category_id'];
        $entityId = $filter['entity_id'];
        $projectId = $filter['project_id'];
        $page = $param['page'] ?? [1, C('default_page_total')];

        $field = [
            'plan.id', 'plan.start_time', 'plan.end_time', 'plan.entity_id', 'plan.type', 'plan.step_category_id', 'plan.task_id',
            'task.id', 'task.name', 'task.description'
        ];
        $planList = model(PlanModel::class)
            ->join('left join task on task.id = plan.task_id')
            ->where(['plan.project_id' => $projectId, 'plan.step_category_id' => $stepCategoryId, 'plan.entity_id' => $entityId, 'plan.type' => ["IN", ['step_category', 'to_do']]])
            ->field(build_complex_query_fields($field))
            ->page($page[0], $page[1])
            ->select();

        $taskIds = [];
        $stepCategoryPlan = [];
        foreach ($planList as $planIndex => $planItem) {
            $planItem = separate_complex_query_data($planItem);

            if (!empty($planItem['task']['id'])) {
                $taskIds[$planItem['task']['id']] = $planItem['task']['id'];
            } else {
                unset($planItem['task']);
            }

            $planList[$planIndex] = $planItem;
            if ($planItem['plan']["type"] === PlanModel::TYPE_STEP_CATEGORY) {
                $stepCategoryPlan = $planItem;
            }
        }

        $stepCategory = model(StepCategoryModel::class)->field("id,name")->find($stepCategoryId);

        $taskMemberMap = [];
        if (!empty($taskIds)) {
            $taskMemberList = model(TaskMemberModel::class)->where(['task_id' => ["IN", $taskIds]])->select();
            $userIds = array_unique(array_column($taskMemberList, 'user_id'));
            $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->field("id,name,avatar")->select();
            $userList = array_column($userList, null, 'id');
            foreach ($taskMemberList as $taskMemberItem) {
                if (isset($userList[$taskMemberItem['user_id']])) {
                    $taskMemberMap[$taskMemberItem['task_id']][] = $userList[$taskMemberItem['user_id']];
                }
            }
        }

        $planList = array_map(function ($planItem) use ($taskMemberMap, $stepCategory) {
            if ($planItem['plan']["type"] === PlanModel::TYPE_TO_DO && isset($taskMemberMap[$planItem['task']['id']])) {
                $planItem['user_list'] = $taskMemberMap[$planItem['task']['id']];
            }

            if ($planItem['plan']["type"] === PlanModel::TYPE_STEP_CATEGORY) {
                $planItem['task']["name"] = $stepCategory['name'];
            }

            return $planItem;
        }, $planList);

        if (empty($stepCategoryPlan)) {
            array_unshift($planList, [
                "plan" => ['start_time' => 0, 'end_time' => 0, 'type' => 'step_category', 'entity_id' => $entityId, 'step_category_id' => $stepCategoryId, 'project_id' => $projectId],
                "task" => ['name' => $stepCategory['name']]
            ]);
        }

        return $planList;
    }

    /**
     * 获取计划项目集下的统计
     * @param $param
     * @return array
     */
    public function getPlanProjectEpisodeCount($param)
    {
        $filter = $param['filter'];
        $projectId = $filter['project_id'];
        $stepCategoryId = $filter['step_category_id'];
        $page = $param['page'] ?? [1, C('default_page_total')];


        $entityFilter = [
            "entity.module_id" => module_by_code("episode")['id'],
            'entity.project_id' => $projectId,
        ];

        if (isset($filter['is_schedule'])) {
            $isSchedule = $filter['is_schedule'] === "yes" ? "COUNT(plan.id)>0" : "COUNT(plan.id)=0";
            $planScope = array_map(function ($item) {
                return "'$item'";
            }, PlanModel::TYPE_PLAN_SCOPE);

            $planScope = join(",", $planScope);

            $rows = model(EntityModel::class)
                ->join("left join plan on plan.entity_id = entity.id and plan.type in ({$planScope}) and plan.step_category_id = {$stepCategoryId}")
                ->where($entityFilter)
                ->field("entity.id,entity.code,count(plan.id) as count")
                ->page($page[0], $page[1])
                ->group("entity.id")
                ->having($isSchedule)
                ->order("LENGTH(entity.code) asc,entity.code asc")
                ->select();
        } else {
            $rows = model(EntityModel::class)
                ->where($entityFilter)
                ->field("id,code")
                ->page($page[0], $page[1])
                ->order("LENGTH(code) asc,code asc")
                ->select();
            if (empty($rows)) {
                return [];
            }
            $episodeIds = array_column($rows, "id");

            $planCount = model(PlanModel::class)->where([
                'plan.entity_id' => ["IN", $episodeIds],
                'plan.project_id' => $projectId,
                'plan.step_category_id' => $stepCategoryId,
                'plan.type' => ["IN", ['step_category', 'to_do']]
            ])->field("entity_id,count(id) as count")->group("entity_id")->select();

            $planCount = array_column($planCount, null, "entity_id");

            $rows = array_map(function ($rowsItem) use ($planCount) {
                $rowsItem['count'] = $planCount[$rowsItem['id']]['count'] ?? 0;
                return $rowsItem;
            }, $rows);
        }
        return $rows;
    }

    /**
     * 获取当前计划项目列表
     * @param $param
     * @param $userId
     * @param bool $isGetAll
     * @return array
     * @throws \Exception
     */
    public function getStepCategoryPlanProjectList($param, $userId, $isGetAll = false)
    {
        $filter = $param['filter'];
        $projectId = $filter['project_id'] ?? [];
        $stepCategoryId = $filter['step_category_id'];
        $page = $param["page"] ?? [1, C('default_page_total')];
        if ($isGetAll) {
            $page = $param["page"] ?? [1, C("database.database_max_select_rows")];
        }

        $projectTemplateList = model(ProjectTemplateModel::class)
            ->field("id,JSON_UNQUOTE(JSON_EXTRACT(config,'$.producer_step_category_config')) as setting")
            ->select();

        $projectTemplateList = array_filter($projectTemplateList, function ($projectTemplateItem) use ($stepCategoryId) {
            if (empty($projectTemplateItem['setting'])) {
                return true;
            }
            $setting = json_decode($projectTemplateItem['setting'], true);
            $isShow = false;
            foreach ($setting as $settingItem) {
                if ($settingItem["id"] === $stepCategoryId && $settingItem["is_show"] == "yes") {
                    $isShow = true;
                    break;
                }
            }
            return $isShow;
        });

        if (empty($projectTemplateList)) {
            return [];
        }

        $projectTemplateIds = array_column($projectTemplateList, 'id');
        $projectFilter = ["project.project_template_id" => ["IN", $projectTemplateIds]];

        if (!empty($projectId)) {
            $projectFilter["project.id"] = $projectId;
        } else {
            $projectIdList = ProjectService::getInstance()->getProjectIdFilterByProjectMember($userId);
            if (empty($projectIdList)) {
                return [];
            }
            $projectFilter["project.id"] = ['IN', implode(',', $projectIdList)];
        }

        $statusCorrespond = ProjectService::getInstance()->filterByStatus("inprogress");
        $statusData = model(StatusModel::class)->selectData([
            "filter" => ["correspond" => ["IN", $statusCorrespond]],
            "fields" => "id",
        ]);

        $statusData = array_column($statusData['rows'], 'id', 'id');
        $projectFilter["JSON_UNQUOTE(JSON_EXTRACT(json,'$.project_status'))"] = ["IN", join(',', $statusData)];

        if (isset($filter['is_schedule'])) {
            $isSchedule = $filter['is_schedule'] === "yes" ? "COUNT(plan.id)>0" : "COUNT(plan.id)=0";
            $planScope = array_map(function ($item) {
                return "'$item'";
            }, PlanModel::TYPE_PLAN_SCOPE);

            $planScope = join(",", $planScope);
            $projectList = model(ProjectModel::class)
                ->join("left join plan on plan.project_id = project.id and plan.type in ({$planScope}) and plan.step_category_id = {$stepCategoryId}")
                ->where($projectFilter)
                ->field(["project.id", "project.name"])
                ->group("project.id")
                ->having($isSchedule)
                ->page($page[0], $page[1])
                ->select();
        } else {
            $projectList = model(ProjectModel::class)
                ->where($projectFilter)
                ->field(["project.id", "project.name"])
                ->page($page[0], $page[1])
                ->select();
        }

        return $projectList;
    }

    /**
     * 多项目计划列表
     * @param $param
     * @param $userId
     * @return array
     * @throws \Exception
     */
    public function getMultiProjectPlanList($param, $userId)
    {
        $projectId = $param['filter']['project_id'] ?? [];
        $month = $param['filter']['month'];

        if (empty($projectId)) {
            $projectList = ProjectService::getInstance()->getUserProjectList($userId, ['param' => ['project_status' => 'inprogress']]);
            if (empty($projectList['rows'])) {
                return [];
            }

            $projectIds = [];
            foreach ($projectList['rows'] as $projectRow) {
                $projectIds[] = $projectRow["project"]["id"];
                $projectMap[$projectRow["project"]["id"]] = $projectRow["project"]["name"];
            }

            $projectId = ["IN", $projectIds];
        } else {
            $projectList = model(ProjectModel::class)->where(["id" => $projectId])->field("id,name")->select();
            $projectMap = array_column($projectList, "name", "id");
        }

        $monthWhere = [];
        foreach ($month as $monthIndex => $monthItem) {
            $currentMonth = strtotime(date("Y-m", strtotime($monthItem)));
            $currentNextMonth = strtotime("+1 month", $currentMonth) - 1;
            $month[$monthIndex] = ['month_start' => $currentMonth, 'month_end' => $currentNextMonth];
            $monthWhere[] = [
                'start_time' => ["ELT", $currentNextMonth], 'end_time' => ["EGT", $currentMonth],
            ];
        }
        $monthWhere["_logic"] = "OR";

        $planFilter = [
            "project_id" => $projectId,
            "type" => ["IN", ['step_category', 'to_do']],
            [$monthWhere],
            "_logic" => "AND"
        ];

        $stepCategoryList = model(StepCategoryModel::class)
            ->where(['is_show' => 'yes'])
            ->field("id,code,name,is_show,is_enable_census,show_name,show_index,color")
            ->select();

        if (empty($stepCategoryList)) {
            return [];
        }
        $stepCategoryIds = [];
        foreach ($stepCategoryList as $item) {
            $stepCategoryIds[] = $item['id'];
        }

        $stepCategoryList = array_column($stepCategoryList, null, "id");

        $planList = model(PlanModel::class)
            ->where($planFilter)
            ->where(['step_category_id' => ['IN', $stepCategoryIds]])
            ->field("id,step_category_id,project_id,start_time,end_time,entity_id")
            ->select();

        if (empty($planList)) {
            return [];
        }

        $monthResult = [];
        foreach ($month as $monthDate) {
            foreach ($planList as $planItem) {
                if (
                    !($planItem['end_time'] < $monthDate["month_start"] || $planItem['start_time'] > $monthDate["month_end"])
                ) {

                    if (empty($monthResult[$monthDate["month_start"]][$planItem['step_category_id']][$planItem['project_id']])) {
                        $monthResult[$monthDate["month_start"]][$planItem['step_category_id']][$planItem['project_id']] =
                            ['id' => $planItem['project_id'], 'name' => $projectMap[$planItem['project_id']] ?? "", 'count' => 1];
                    } else {
                        $monthResult[$monthDate["month_start"]][$planItem['step_category_id']][$planItem['project_id']]['count'] += 1;
                    }
                }
            }
        }

        $result = [];
        foreach ($monthResult as $monthResultIndex => $monthResultItems) {
            $stepCategoryData = [];
            foreach ($monthResultItems as $stepCategoryId => $monthResultItem) {
                $stepCategoryData[$stepCategoryList[$stepCategoryId]["code"]] = [
                    'id' => $stepCategoryId,
                    'code' => $stepCategoryList[$stepCategoryId]["code"],
                    'project_list' => array_values($monthResultItem)
                ];
            }
            $result[] = [
                "month" => $monthResultIndex,
                "row" => $stepCategoryData
            ];
        }

        return $result;
    }

    /**
     * 保存工序分类计划
     * @param $data
     * @param $userId
     * @param $tenantId
     * @param bool $isValidatePlan
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function saveStepCategoryPlan($data, $userId, $tenantId, $isValidatePlan = true)
    {

        $planModel = model(PlanModel::class);

        $planData = $data["plan"];

        $deletePlan = [];
        if (empty($planData['id'])) {
            //处理创建工序分类计划数据
            $plan = $this->dealCreateStepCategoryPlanData($data, $planData, $isValidatePlan, $deletePlan);
        } else {
            $plan = model(PlanModel::class)->find($planData['id']);

            $producerStepCategoryList = ProjectService::getInstance()->getShowStepCategoryList($plan['project_id']);
            $data['plan'] = array_merge($plan, $data['plan']);
            $this->validatePlan($data, $producerStepCategoryList, $isValidatePlan);

            if (!empty($plan)) {
                $deletePlan = [$plan];
            }
            $planData = array_merge($plan, $planData);
            /**
             * 判定 工序分类标签更改情况,与集更改情况
             * 1 . 如果只是在本集内改动 工序分类 ,需要判定是否与其他的工序分类进行了冲突,如不能覆盖则进行报错.反之就行覆盖, 在将当前的删掉 ,如不删掉会出现一集中存在两个相同的分类的环节制作
             * 2. 如果是移动其他集
             *  2-1.需要判定是否与其他的工序分类进行了冲突,如不能覆盖则进行报错.反之就行覆盖, 在将当前的删掉
             *  2-2. 如果不存在冲突 ,则将当前计划的集更改到移动的集上
             */

            $isDeletePlan = false;
            if ($plan['scope'] === PlanModel::SCOPE_SINGLE && $plan['type'] == PlanModel::TYPE_STEP_CATEGORY) {
                // 获取当前集 与 指定集
                $currentEpisodeId = 0;
                $assignedEpisodeId = 0;
                if (!empty($plan['entity_id'])) {
                    $currentEpisodeId = $plan['entity_id'];
                }

                if (!empty($planData['entity_id'])) {
                    $assignedEpisodeId = $planData['entity_id'];
                }

                $relateTagLinkIds = [];

                $needUpdateRelateTag = [];
                // 如果没传入标签,则查询当前计划的标签
                if (isset($data["relate_tag"])) {
                    foreach ($data["relate_tag"] as $relateTagItem) {
                        if ($relateTagItem['link_module_code'] == 'episode') {
                            $assignedEpisodeId = $relateTagItem['link_id'];
                        }

                        if (in_array($relateTagItem['link_module_code'], ['show_step_category', 'step_category'])) {
                            $relateTagLinkIds[] = $relateTagItem['link_id'];
                        }
                    }

                    $needUpdateRelateTag = $data["relate_tag"];
                    $planData['entity_id'] = $assignedEpisodeId;
                } else {
                    $taskRelationTagList = model(TaskRelationTagModel::class)->where([
                        'task_id' => $plan['task_id']
                    ])->select();

                    $needUpdateRelateTag = $taskRelationTagList;
                    foreach ($taskRelationTagList as $relateTagItem) {
                        if (in_array($relateTagItem['link_module_code'], ['show_step_category', 'step_category'])) {
                            $relateTagLinkIds[] = $relateTagItem['link_id'];
                        }
                    }
                }

                $planList = model(PlanModel::class)
                    ->where(['project_id' => $plan['project_id'], 'type' => PlanModel::TYPE_STEP_CATEGORY, 'entity_id' => $assignedEpisodeId, 'scope' => PlanModel::SCOPE_SINGLE,])
                    ->field("id,task_id,end_time,project_id,start_time,type,scope")
                    ->select();

                if (!empty($currentEpisodeId) && !empty($assignedEpisodeId)) {
                    $planTaskIds = [];
                    $planTaskMap = [];
                    if ($currentEpisodeId == $assignedEpisodeId) {
                        // 当前集比较 与其他分类的
                        foreach ($planList as $planItem) {
                            if ($plan['task_id'] != $planItem['task_id']) {
                                $planTaskIds[] = $planItem['task_id'];
                            }
                            $planTaskMap[$planItem['task_id']] = $planItem;
                        }

                        // 如果 环节标签有更改
                        if (isset($data["relate_tag"])) {
                            $exactlySameTaskId = $this->detectionCompleteCoverage($planTaskIds, $relateTagLinkIds);

                            if (!empty($exactlySameTaskId)) {
                                $exactlySameTaskPlan = $planTaskMap[$exactlySameTaskId];
                                //内容覆盖 , 去除掉不能被覆盖的参数 id,task_id
                                unset($planData['id'], $planData['task_id']);
                                $planData = array_merge($exactlySameTaskPlan, $planData);
                                $task = model(TaskModel::class)->field("id,name,description,plan_end_time,plan_start_time,step_type")->find($planData['task_id']);
                                // 待办覆盖
                                $data["task"] = array_merge($task, $data["task"]);
                                $isDeletePlan = true;
                            }
                        }
                    } else {
                        foreach ($planList as $planItem) {
                            // 跳过当前
                            $planTaskIds[] = $planItem['task_id'];
                            $planTaskMap[$planItem['task_id']] = $planItem;
                        }

                        $exactlySameTaskId = $this->detectionCompleteCoverage($planTaskIds, $relateTagLinkIds);

                        if (!empty($exactlySameTaskId)) {
                            $exactlySameTaskPlan = $planTaskMap[$exactlySameTaskId];
                            //内容覆盖 , 去除掉不能被覆盖的参数 id,task_id
                            unset($planData['id'], $planData['task_id']);
                            $planData = array_merge($exactlySameTaskPlan, $planData);
                            $task = model(TaskModel::class)->field("id,name,description,plan_end_time,plan_start_time,step_type")->find($planData['task_id']);
                            // 待办覆盖
                            $data["task"] = array_merge($task, $data["task"]);
                            $isDeletePlan = true;
                        } else {
                            $planData['entity_id'] = $assignedEpisodeId;
                            // 将标签的里的剧集id 改掉

                            foreach ($needUpdateRelateTag as &$needUpdateRelateTagItem) {
                                if ($needUpdateRelateTagItem['link_module_code'] == 'episode') {
                                    $needUpdateRelateTagItem['link_id'] = $assignedEpisodeId;
                                }
                            }
                            $data["relate_tag"] = $needUpdateRelateTag;
                        }
                    }
                }
            }

            //如果不能删除将其置空
            if (empty($isDeletePlan)) {
                $deletePlan = [];
            }
            // 集标签与 entity_id 需要同步
        }

        $relateEpisodeTagList = [];
        $oldTaskEpisodeRelationList = [];
        $needRemoveTaskIdList = [];
        $needAddTaskEpisodeIdList = [];
        $taskBaseRelateTag = [];
        if (!empty($plan['id'])) {
            $oldTaskEpisodeRelationList = model(PlanTaskRelationModel::class)
                ->join('task on task.id = plan_task_relation.task_id')
                ->join("task_relation_tag r_episode on task.id = r_episode.task_id and r_episode.link_module_code = 'episode'")
                ->where(['plan_task_relation.plan_id' => $plan['id']])
                ->field('task.id,max(r_episode.link_id) episode_id')
                ->group('task.id')
                ->select();
        }

        if (isset($data['relate_tag'])) {
            // 对比relate_tag 来计算是否需要删除或者新增待办
            $relateEpisodeIdList = [];
            $removeEpisodeTagList = [];
            foreach ($data['relate_tag'] as $relateTag) {
                if ($relateTag['link_module_code'] === 'episode') {
                    $relateEpisodeTagList[$relateTag['link_id']] = $relateTag;
                    $relateEpisodeIdList[] = $relateTag['link_id'];
                } else {
                    $removeEpisodeTagList[] = $relateTag;
                }
            }
            $data['relate_tag'] = $removeEpisodeTagList;
            if (!empty($plan['id'])) {
                $oldEpisodeIdList = array_column($oldTaskEpisodeRelationList, 'episode_id');
                foreach ($oldTaskEpisodeRelationList as $oldTaskEpisodeRelation) {
                    if (!in_array($oldTaskEpisodeRelation['episode_id'], $relateEpisodeIdList)) {
                        $needRemoveTaskIdList[] = $oldTaskEpisodeRelation['id'];
                    }
                    $taskBaseRelateTag[$oldTaskEpisodeRelation['id']] = [
                        'link_module_code' => 'episode',
                        'link_id' => $oldTaskEpisodeRelation['episode_id'],
                    ];
                }
                foreach ($relateEpisodeIdList as $relateEpisodeId) {
                    if (!in_array($relateEpisodeId, $oldEpisodeIdList)) {
                        $needAddTaskEpisodeIdList[] = $relateEpisodeId;
                    }
                }
            } else {
                $needAddTaskEpisodeIdList = $relateEpisodeIdList;
            }
        }

        $result = [];
        $planModel->startTrans();
        try {
            //如果为空,则进行新增
            if (empty($plan)) {
                //同步项目id
                $data["task"]['project_id'] = $planData['project_id'];
                $executor = $data["executor"] ?? null;
                $partner = $data["partner"] ?? null;
                $taskData = [
                    "task" => $data["task"],
                    "executor" => $executor,
                    "partner" => $partner
                ];

                if (isset($data['relate_tag'])) {
                    $taskData['relate_tag'] = $data['relate_tag'];
                }

                // 执行人模板待办
                $taskData['executor_list'] = $data['executor_list'] ?? null;
                // 发起人模板待办
                $taskData['starter_list'] = $data['starter_list'] ?? null;

                // 模板待办id
                $taskData['template_todo_id'] = $data['template_todo_id'] ?? null;
                // 排期任务媒体文件
                $taskData['task_cos_media'] = $data['task_cos_media'] ?? [];

                if ($data['task']['step_type'] == TaskModel::STEP_TYPE_STANDARD_MAKE_TO_DO) {
                    $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
                    $taskData['task']['task_status'] = $closedStatus['id'];
                }
                $planTaskRelationList = [];
//                if (isset($data['plan']['end_time'])) {
//                    $taskData['task']['plan_delivery_time'] = strtotime($data['plan']['end_time']);
//                }
                if (isset($data['plan']['task_stage_id'])) {
                    $taskData['task']['task_stage_id'] = $data['plan']['task_stage_id'];
                }
                if (!empty($needAddTaskEpisodeIdList)) {
                    foreach ($needAddTaskEpisodeIdList as $needAddTaskEpisodeId) {
                        $currentTaskData = $taskData;
                        $currentTaskData['relate_tag'][] = $relateEpisodeTagList[$needAddTaskEpisodeId];
                        $result = TaskMemberService::getInstance()->create($currentTaskData, $userId, $tenantId);
                        $planTaskRelationList[] = [
                            'task_id' => $result['task']['id'],
                        ];
                    }
                } else {
                    $result = TaskMemberService::getInstance()->create($taskData, $userId, $tenantId);
                    $planTaskRelationList[] = [
                        'task_id' => $result['task']['id'],
                    ];
                }

                $planData['task_id'] = $result['task']['id'];
                $result['plan'] = $planModel->addItem($planData);
                foreach ($planTaskRelationList as &$planTaskRelationItem) {
                    $planTaskRelationItem['plan_id'] = $result['plan']['id'];
                    $planTaskRelationItem['created'] = time();
                    $planTaskRelationItem['created_by'] = $userId;
                }
                model(PlanTaskRelationModel::class)->addAll($planTaskRelationList);
            } else {
                if (!empty($planData)) {
                    $result['plan'] = model(PlanModel::class)->modifyItem($planData);
                    if (empty($result['plan'])) {
                        $result['plan'] = $plan;
                    } else {
                        $result['plan'] = array_merge($plan, $result['plan']);
                    }
                } else {
                    $result['plan'] = $plan;
                }
                if (!empty($data["task"]) && !empty($data['task']['id'])) {
                    if (isset($data['relate_tag']) && isset($taskBaseRelateTag[$data['task']['id']])) {
                        $data['relate_tag'][] = $taskBaseRelateTag[$data['task']['id']];
                    }
                    $data["task"] = TaskMemberService::getInstance()->updateTask($data, $userId)['task'];
                }

                // 检查是否需要同步计划时间到任务上
                $updatePlanData = [];
                if (!empty($result['plan']['start_time'])) {
                    $updatePlanData['plan_start_time'] = $result['plan']['start_time'];
                }
                if (!empty($result['plan']['end_time'])) {
                    $updatePlanData['plan_end_time'] = $result['plan']['end_time'];
//                    $updatePlanData['plan_delivery_time'] = $result['plan']['end_time'];
                }
                if (isset($data['plan']['task_stage_id'])) {
                    $updatePlanData['task_stage_id'] = $data['plan']['task_stage_id'];
                }
                if (isset($planData['name'])) {
                    // 修改plan名称 同步到task
                    $updatePlanData['name'] = $planData['name'];
                }
                if (!empty($updatePlanData) && !empty($oldTaskEpisodeRelationList)) {
//                    model(TaskModel::class)->where(['id' => ['IN', implode(',', array_column($oldTaskEpisodeRelationList, 'id'))]])->save($updatePlanData);
                    $updatePlanData['id'] = array_column($oldTaskEpisodeRelationList, 'id');
                    CommonService::instance('task')->commonBatchUpdateSame($updatePlanData, module_by_code('task'));
                }
                // 新增关联  复制待办任务
                if (!empty($needAddTaskEpisodeIdList) && !empty($oldTaskEpisodeRelationList)) {
                    TaskMemberService::getInstance()->copyPlanTaskToEpisodeIdList($plan['id'], array_last($oldTaskEpisodeRelationList)['id'], $needAddTaskEpisodeIdList);
                }

                //删除掉多余计划
                if (!empty($deletePlan)) {
                    $this->deletePlan($deletePlan);
                }
            }
            //添加版本
            if (isset($data['plan_source']) && $data['plan_source'] == "demand_order") {
                $this->addPlanVersion($data['plan_source'], $result['plan'], $plan);
            }
            if (!empty($needRemoveTaskIdList)) {
                model(TaskModel::class)->where(['id' => ['IN', implode(',', $needRemoveTaskIdList)]])->delete();
                model(PlanTaskRelationModel::class)->where(['task_id' => ['IN', implode(',', $needRemoveTaskIdList)]])->delete();
                model(TaskMemberModel::class)->where(['task_id' => ['IN', implode(',', $needRemoveTaskIdList)]])->delete();
                model(TaskMemberRelationModel::class)->where(['task_id' => ['IN', implode(',', $needRemoveTaskIdList)]])->delete();
            }
            $planModel->commit();
        } catch (\Throwable $e) {
            $planModel->rollback();
            throw $e;
        }

        $affectedPlanIds = [];
        if (!empty($data['affected_plan'])) {
            // 同步影响
            $affectedPlanIds = $this->planSyncEffectPlan($result['plan'], $userId, $tenantId);
        }
        //加入本身id
        $affectedPlanIds[] = $result['plan']['id'];
        $result['affected_plan_ids'] = $affectedPlanIds;
        return $result;
    }

    /**
     * 获取工序分类计划详情
     * @param $param
     * @param $userId
     * @return array|false|mixed|string|null
     * @throws \Exception
     */
    public function getStepCategoryPlanDetail($param, $userId)
    {
        $filter = $param["filter"];
        $plan = model(PlanModel::class)->where($filter)->find();

        $stepCategory = model(StepCategoryModel::class)->find($plan['step_category_id']);
        $entity = model(EntityModel::class)->find($plan['entity_id']);

        $plan["step_category_name"] = $stepCategory["name"] ?? "";
        $plan["entity_name"] = $entity["name"] ?? "";

        if ($plan["type"] === PlanModel::TYPE_TO_DO) {
            $toDoTask = TaskMemberService::getInstance()->find([
                "filter" => [
                    "id" => $plan["task_id"]
                ]
            ], $userId);
            $plan["to_do"] = $toDoTask;
        }

        return $plan;
    }

    /**
     * 未排期数据
     * @param $param
     * @param $StepCategoryIds
     * @param $startTime
     * @param $endTime
     * @param $planType
     * @param $taskName
     * @param bool $showGlobal
     * @return array
     * @throws Exception
     */
    public function getPlanOneProject($param, $StepCategoryIds, $startTime, $endTime, $planType, $taskName, $showGlobal = true)
    {
        $filter = $param['filter'];
        $projectId = $filter['project_id'] ?? '';
        $episodeCode = $filter['code'] ?? '';
        $isShowSealEpisode = ''; // 是否显示播出集数 yes、no
        unset($filter['project_id'], $filter['code']);

        if (isset($filter['is_show_seal_episode'])) {
            if (in_array($filter['is_show_seal_episode'], ['yes', 'no'])) {
                $isShowSealEpisode = $filter['is_show_seal_episode'];
            }
            unset($filter['is_show_seal_episode']);
        }

        // 查询当前项目所在的项目模板
        $projectData = $this->projectModel->where(['id' => $projectId])->find();
        if (empty($projectData)) {
            return [];
        }
        $page = $param['page'] ?? [1, C('default_page_total')];

        $result = [];
        $planFilter = [];
        $globalFilter = [];

        // 排期时间筛选
        if (!empty($startTime) && !empty($endTime)) {
            $monthWhere[] = [
                'plan.start_time' => ["ELT", $endTime], 'plan.end_time' => ["EGT", $startTime]
            ];
            $planFilter[] = $monthWhere;
            $filter[] = $monthWhere;
            $globalFilter[] = $monthWhere;
        }

        if (!empty($episodeCode)) {
            $filter[]['entity.code'] = $episodeCode;
        }

        // 排期类型筛选
        if (!empty($planType)) {
            $planFilter[]['plan.type'] = ['IN', $planType];
            $filter[]['plan.type'] = ['IN', $planType];
            $globalFilter[]['plan.type'] = ['IN', $planType];
        }

        //获取--排期环节组装---团队环节
        list($showStepCategoryIdProjectIds, $showStepCategoryIdProjectList, $group, $teamStepCategoryNameMap) = ProjectTeamService::getInstance()->clearPlanStepCategory($StepCategoryIds);

        $group = array_values($group);
        if (empty($showStepCategoryIdProjectIds)) {
            return $result;
        }

        // 环节筛选
        if (!empty($StepCategoryIds)) {
            $planFilter[]['r_step_category.link_id'] = ['IN', $StepCategoryIds];
            $filter[]['r_step_category.link_id'] = ['IN', $StepCategoryIds];
            $globalFilter[]['r_step_category.link_id'] = ['IN', $StepCategoryIds];
        } else {
            $showStepCategoryIdProjectIdString = join(",", $showStepCategoryIdProjectIds);
            $planFilter[] = [
                '_string' => "r_step_category.link_id is null or (r_step_category.link_id in ({$showStepCategoryIdProjectIdString}))"
            ];
            $filter[] = [
                '_string' => "r_step_category.link_id is null or (r_step_category.link_id in ({$showStepCategoryIdProjectIdString}))"
            ];
            $globalFilter[] = [
                '_string' => "r_step_category.link_id is null or (r_step_category.link_id in ({$showStepCategoryIdProjectIdString}))"
            ];
        }

        if (!empty($taskName)) {
            $planFilter[]['task.name'] = $taskName;
            $filter[]['task.name'] = $taskName;
            $globalFilter[]['task.name'] = $taskName;
        }

        // 项目筛选
        if (!empty($projectId)) {
            $planFilter[]['plan.project_id'] = $projectId;
            $filter[]['entity.project_id'] = $projectId;
            $globalFilter[]['plan.project_id'] = $projectId;
        }
        // 查剧集 ,隐藏已播出的剧集 ,默认按创建时间排序
        $order = [
//            "if(JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_plan_seal_time')) is null, 2999999999,JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_plan_seal_time'))) asc",
            "substring(entity.code,1,2) desc",
            "length(entity.code) asc",
            "entity.code asc"
        ];

        $episodeEntityWhereArr = [
            'entity.module_id' => module_by_code('episode')['id'],
        ];

        if ($isShowSealEpisode == 'yes') {
            $episodeIsShowString = "(JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_is_show'))='yes')";
        } elseif ($isShowSealEpisode == 'no') {
            $episodeIsShowString = "(JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_is_show')) is null or JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_is_show'))='no')";
        } else {
            $episodeIsShowString = '';
        }

        if ($episodeIsShowString) {
            $episodeEntityWhereArr['_string'] = $episodeIsShowString;
        }

        $episodeEntityData = $this->entityModel
            ->join('plan on plan.entity_id = entity.id', 'LEFT')
            ->join('plan_task_relation on plan_task_relation.plan_id = plan.id', 'LEFT')
            ->join('task on plan_task_relation.task_id = task.id', 'LEFT')
            ->join("task_relation_tag r_step_category on r_step_category.task_id = plan.task_id and r_step_category.link_module_code in ('step_category','show_step_category')", 'LEFT')
            ->where($filter)
            ->where($episodeEntityWhereArr)
            ->group('entity.id')
            ->field("entity.id")
            ->order($order)
            ->page($page[0], $page[1])
            ->select();

        if (!empty($episodeEntityData)) {
            $episodeIdList = array_column($episodeEntityData, 'id');

            $episodeEntityInEpisodeIdWhereArr = [
                'entity.module_id' => module_by_code('episode')['id'],
                'entity.id' => ['IN', $episodeIdList]
            ];

            if ($episodeIsShowString) {
                $episodeEntityInEpisodeIdWhereArr['_string'] = $episodeIsShowString;
            }

            $episodeEntityData = $this->entityModel
                ->where($episodeEntityInEpisodeIdWhereArr)
                ->field("entity.code,entity.id,entity.project_id,JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_plan_seal_time')) as episode_plan_seal_time,JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_plan_show_time')) as episode_plan_show_time,
            if(JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_plan_seal_time')) is null, 2999999999,JSON_UNQUOTE(JSON_EXTRACT(entity.json,'$.episode_plan_seal_time'))) as order_episode_plan_seal_time,
            substring(entity.code,1,2) as order_code")
                ->order([
//                    "order_episode_plan_seal_time asc",
                    "order_code desc", "length(entity.code) asc", "entity.code asc"])
                ->select();
        }


        $isFirstPage = $page[0] == 1;
        if ($isFirstPage && $showGlobal) {
            $globalEpisodeData = [
                'id' => 'global',
                'code' => 'global',
                'project_id' => $projectId,
                'episode_plan_seal_time' => null,
                'episode_plan_show_time' => null,
                'order_episode_plan_seal_time' => null,
            ];
            array_unshift($episodeEntityData, $globalEpisodeData);
        }

        if (empty($episodeEntityData)) {
            return $result;
        }

        $episodeEntityIds = [];
        $episodeEntityMap = [];
        foreach ($episodeEntityData as $episodeEntityDataIndex => $episodeEntityDataItem) {
            if ($episodeEntityDataItem['id'] != 'global') {
                $episodeEntityIds[] = $episodeEntityDataItem['id'];
                $episodeEntityMap[$episodeEntityDataItem['id']] = $episodeEntityDataItem;
            }

            if ($episodeEntityDataItem['order_episode_plan_seal_time'] == 2999999999) {
                $episodeEntityDataItem['order_episode_plan_seal_time'] = null;
            }
            $episodeEntityDataItem['project_name'] = $projectData['name'];
            $episodeEntityDataItem['step_category_list'] = $group;
            $episodeEntityData[$episodeEntityDataIndex] = $episodeEntityDataItem;
        }

        $planFilter[] = [
            "task_relation_tag.link_id" => ['IN', $episodeEntityIds],
            ["plan.type" => ["IN", PlanModel::TYPE_GROUP]],
            ["plan.type" => ["NEQ", PlanModel::TYPE_MILESTONE]],
            "_logic" => "AND",
        ];

        $result = $episodeEntityData;

        // 查剧单集下的计划
        $planFields = "plan.id,plan.tenant_id,plan.attribute,plan.is_lock,plan.project_id,plan.start_time,plan.end_time,plan.related_entity,plan.type,plan_task_relation.task_id,task_relation_tag.link_id as entity_id,plan.scope";
        $planData = [];

        if (!empty($episodeEntityIds)) {
            $planData = $this->planModel
                ->join('plan_task_relation on plan.id = plan_task_relation.plan_id')
                ->join('task on task.id = plan_task_relation.task_id')
                ->join("task_relation_tag on task_relation_tag.task_id = plan.task_id and task_relation_tag.link_module_code = 'episode'")
                ->join("task_relation_tag r_step_category on r_step_category.task_id = plan.task_id and r_step_category.link_module_code in ('step_category','show_step_category')", 'LEFT')
                ->where($planFilter)
                ->where(['plan.scope' => PlanModel::SCOPE_SINGLE])
                ->field($planFields)
                ->select();
        }

        // 查询全局的计划
        if ($isFirstPage && $showGlobal) {
            $planGlobalData = $this->planModel
                ->join('plan_task_relation on plan.id = plan_task_relation.plan_id')
                ->join('task on task.id = plan_task_relation.task_id')
                ->join("task_relation_tag on task_relation_tag.task_id = plan.task_id and task_relation_tag.link_module_code = 'episode'", 'LEFT')
                ->join("task_relation_tag r_step_category on r_step_category.task_id = plan.task_id and r_step_category.link_module_code in ('step_category','show_step_category')", 'LEFT')
                ->where(['plan.project_id' => $projectId, 'plan.scope' => PlanModel::SCOPE_GLOBAL, 'plan.type' => ['NEQ', PlanModel::TYPE_MILESTONE],])
                ->where($globalFilter)
                ->field("plan.id,plan.tenant_id,plan.attribute,plan.is_lock,plan.project_id,plan.start_time,plan.end_time,plan.type,plan.task_id,plan.related_entity,plan.scope")
                ->group("plan.id")
                ->select();
            $planData = array_merge($planData, $planGlobalData);

            if (empty($planData) && empty($planGlobalData)) {
                return $result;
            }
        } else {
            if (empty($planData)) {
                return $result;
            }
        }


        $taskIds = [];
        $singleTaskIds = [];
        $globalTaskIds = $globalPlanIds = [];
        $planIds = [];
        foreach ($planData as $planDataItem) {
            if (in_array($planDataItem['id'], $planIds)) {
                continue;
            }
            $planIds[] = $planDataItem['id'];
            $taskIds[] = $planDataItem["task_id"];
            if ($planDataItem['scope'] == PlanModel::SCOPE_SINGLE) {
                $singleTaskIds[] = $planDataItem["task_id"];
            } else {
                $globalTaskIds[] = $planDataItem["task_id"];
                $globalPlanIds[] = $planDataItem['id'];
            }
        }

        $taskMemberMap = [];
        $taskData = [];
        if (!empty($taskIds)) {
            $taskData = $this->taskModel->field('id,name,plan_start_time,plan_end_time,episode_id,start_time,end_time,description')
                ->where(['id' => ['IN', $taskIds]])
                ->select();
            $taskData = array_column($taskData, null, "id");

            //查询代办人员
            $taskMemberList = $this->taskMemberModel->where(['task_id' => ["IN", $taskIds]])->select();
            $userIds = array_unique(array_column($taskMemberList, 'user_id'));
            if (!empty($userIds)) {
                $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->field("id as saas_id,union_id as id,name,avatar")->select();
                $userList = array_column($userList, null, 'saas_id');
                foreach ($taskMemberList as $taskMemberItem) {
                    if (!empty($userList[$taskMemberItem['user_id']])) {
                        $taskMemberMap[$taskMemberItem['task_id']][] = $userList[$taskMemberItem['user_id']];
                    }
                }
            }

            //查询标签 (标签整合)
            $taskRelationTagFilter = [];
            if (!empty($singleTaskIds)) {
                $taskRelationTagFilter[] = [
                    'task_id' => ["IN", $singleTaskIds],
                    'link_module_code' => ["IN", ['show_step_category', 'step_category']],
                    'link_id' => ["IN", $showStepCategoryIdProjectIds]
                ];
            }

            if (!empty($globalTaskIds)) {
                $taskRelationTagFilter[] = [
                    'task_id' => ["IN", $globalTaskIds],
                    'link_module_code' => ["IN", ['show_step_category', 'step_category', 'episode']],
                ];
            }

            if (!empty($taskRelationTagFilter) && count($taskRelationTagFilter) >= 2) {
                $taskRelationTagFilter["_logic"] = "OR";
            }

            $taskRelationTagList = model(TaskRelationTagModel::class)->where($taskRelationTagFilter)->select();

            $showTaskStepCategoryMap = [];
            $taskEpisodeCountMap = [];
            $globalTaskEpisodeIds = [];
            foreach ($taskRelationTagList as $taskRelationTagItem) {
                //名字判定
                $showName = null;
                $parentName = null;
                if (isset($showStepCategoryIdProjectList[$taskRelationTagItem['link_id']])) {
                    //获取团队环节的--名称
                    $parentName = $teamStepCategoryNameMap[$taskRelationTagItem['link_id']] ?? $showStepCategoryIdProjectList[$taskRelationTagItem['link_id']]['show_name'];
                    if ($taskRelationTagItem['link_module_code'] == 'show_step_category') {
                        $showName = $showStepCategoryIdProjectList[$taskRelationTagItem['link_id']]['show_name'] ?? '';
                    } elseif ($taskRelationTagItem['link_module_code'] == 'step_category') {
                        $showName = $showStepCategoryIdProjectList[$taskRelationTagItem['link_id']]['name'] ?? '';
                    }
                }


                //名字映射
                if (!empty($showName)) {
                    if (!isset($showTaskStepCategoryMap[$taskRelationTagItem['task_id']])) {
                        $showTaskStepCategoryMap[$taskRelationTagItem['task_id']] = ['show_name' => [$showName], 'parent_name' => $parentName];
                    } elseif (!in_array($showName, $showTaskStepCategoryMap[$taskRelationTagItem['task_id']]['show_name'])) {
                        $showTaskStepCategoryMap[$taskRelationTagItem['task_id']]['show_name'][] = $showName;
                    }
                }

                // 获取全局的剧集

                if (in_array($taskRelationTagItem['task_id'], $globalTaskIds)) {
                    if (in_array($taskRelationTagItem['link_module_code'], ['episode'])) {
                        $globalTaskEpisodeIds[] = $taskRelationTagItem['link_id'];

                        $taskEpisodeCountMap[$taskRelationTagItem['task_id']][$taskRelationTagItem['link_id']] = $taskRelationTagItem['link_id'];
                    }
                }
            }
        }

        $globalTaskEpisodeMap = [];
        if (!empty($globalTaskEpisodeIds)) {
            $globalTaskEpisodeList = model(EntityModel::class)->where(['id' => ["IN", $globalTaskEpisodeIds]])->field("id,code")->select();
            $globalTaskEpisodeMap = array_column($globalTaskEpisodeList, 'code', 'id');
        }

        //获取全局排期下---剧集名列表
        $planEpisodeList = [];
        if ($globalPlanIds) {
            $planEpisodeList = $this->getPlanEpisodeList($globalPlanIds);
        }

        $planGroup = [];
        foreach ($planData as $item) {
            $item['label'] = $showTaskStepCategoryMap[$item['task_id']]['show_name'] ?? [];
            $currentParentName = $showTaskStepCategoryMap[$item['task_id']]['parent_name'] ?? "";
            $item['user_info'] = $taskMemberMap[$item['task_id']] ?? [];
            $item['project_info'] = [
                'id' => $projectData['id'],
                'name' => $projectData['name']
            ];
            $item["task"] = $taskData[$item["task_id"]] ?? [];

            if (!empty($taskEpisodeCountMap[$item['task_id']])) {
                $globalTaskEpisodeCode = array_intersect_key($globalTaskEpisodeMap, $taskEpisodeCountMap[$item['task_id']]);
                $item['ep_count'] = count($taskEpisodeCountMap[$item['task_id']]) . "集";
                $item['ep_list'] = array_values($globalTaskEpisodeCode);
            }

            if ($item['scope'] == PlanModel::SCOPE_GLOBAL) {
                $item['ep_name'] = 'global';
                $item['ep_id'] = 'global';
                $item['episode_plan_seal_time'] = null;
                $item['episode_plan_show_time'] = null;
                //全局排期--多集情况重新获取---剧集名列表
                $epList = $planEpisodeList[$item['id']] ?? [];
                $item['ep_count'] = count($epList) . "集";
                $item['ep_list'] = $epList;

                if (!empty($currentParentName)) {
                    if (!isset($planGroup['global'])) {
                        $planGroup['global'][$currentParentName] = [$item];
                    } else {
                        $planGroup['global'][$currentParentName][] = $item;
                    }
                }
            } else {
                $item['ep_name'] = $episodeEntityMap[$item['entity_id']]['code'] ?? 'global';
                $item['ep_list'] = [$item['ep_name']];
                $item['ep_id'] = $item['entity_id'];
                $item['episode_plan_seal_time'] = $episodeEntityMap[$item['entity_id']]['episode_plan_seal_time'] ?? null;
                $item['episode_plan_show_time'] = $episodeEntityMap[$item['entity_id']]['episode_plan_show_time'] ?? null;

                if (!empty($currentParentName)) {
                    $planGroup[$item['entity_id']][$currentParentName][] = $item;
                }
            }
        }

        foreach ($episodeEntityData as $episodeEntityIndex => $episodeEntityDataItem) {
            foreach ($episodeEntityDataItem['step_category_list'] as $stepCategoryIndex => $stepCategoryItem) {
                $stepCategoryItem['plan_list'] = $planGroup[$episodeEntityDataItem['id']][$stepCategoryItem['name']] ?? [];
                if (in_array($stepCategoryItem['module_code'], ['asset', 'level', 'design', 'shot', 'storyboard'])) {
                    $stepCategoryItem['plan_list'] = array_map(function ($planItem) use ($stepCategoryItem) {
                        $planItem['count_module_code'] = $stepCategoryItem['module_code'];
                        return $planItem;
                    }, $stepCategoryItem['plan_list']);
                }
                //把排期按属性分组---------------
                if ($stepCategoryItem['attribute_list']) {
                    $planAttrList = array_group_by($stepCategoryItem['plan_list'], 'attribute');
                    foreach ($stepCategoryItem['attribute_list'] as &$val) {
                        if ($val['attribute'] == $stepCategoryItem['name']) {
                            $val['attribute_plan'] = $planAttrList[''] ?? [];
                        } else {
                            $val['attribute_plan'] = $planAttrList[$val['attribute']] ?? [];
                        }
                    }
                    $stepCategoryItem['plan_list'] = [];
                }
                //把排期按属性分组-------end--------
                $episodeEntityDataItem['step_category_list'][$stepCategoryIndex] = $stepCategoryItem;
            }
            $episodeEntityData[$episodeEntityIndex] = $episodeEntityDataItem;
        }

//        return [
//            'ep_list' => $episodeEntityData,
//            'plan_list' => $planDataList,
//        ];
        return $episodeEntityData;
    }

    /**
     * 获取排期---剧集名列表
     * @param array $planIds 排期IDs
     * @return array
     */
    public function getPlanEpisodeList(array $planIds)
    {
        if (empty($planIds)) {
            return [];
        }
        //查询排期下的--剧集列表 --- 排期的剧集不会随便改变
        $episodeList = model(PlanTaskRelationModel::class)->alias('ptr')
            ->join("task_relation_tag trt on trt.task_id = ptr.task_id and trt.link_module_code ='episode'")
            ->join('entity on entity.id = trt.link_id')
            ->where(['ptr.plan_id' => ['IN', $planIds]])
            ->field('ptr.plan_id,entity.name')
            ->cache(C('database.default_query_cache_time'))
            ->select();
        //根据排期ID分组
        $planEpisodeList = [];
        foreach ($episodeList as $val) {
            $planEpisodeList[$val['plan_id']][] = $val['name'];
        }
        return $planEpisodeList;
    }

    /**
     * 单项目列表-Find
     * @param $param
     * @return array|false|mixed|string
     * @throws Exception
     */
    public function getPlanOneProjectFind($param)
    {
        $filter = $param['filter'];
        $id = $filter['id'];
        // 查剧集下的计划
        $planData = $this->planModel
            ->where(['id' => $id])
            ->field('id,name,tenant_id,project_id,start_time,end_time,type,task_id,scope,task_stage_id,is_lock,attribute,related_entity,created_by,created')
            ->find();
        if (empty($planData)) {
            return [];
        }
        // 查询项目
        $projectData = model(ProjectModel::class)->where(['id' => $planData['project_id']])->field('id,name,project_template_id')->find();

        // 查询待办任务
        $taskList = $this->taskModel
            ->join('plan_task_relation on task.id = plan_task_relation.task_id')
            ->field("task.id,task.name,task.plan_start_time,task.plan_end_time,task.episode_id,task.start_time,task.end_time,task.description,task.json,JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.template_todo_info')) as template_todo_info,task.created_by")
            ->where(['plan_task_relation.plan_id' => $planData['id']])
            ->select();
        $taskIdList = array_column($taskList, 'id');

        // 审核状态
        $reviewRequest = model(ReviewRequestModel::class)
            ->join('review_request_node on review_request.id = review_request_node.review_request_id')
            ->where(['project_id' => $planData['project_id'], 'module_id' => module_by_code('plan')['id'], 'link_id' => $planData['id']])
            ->field('review_request.id,review_request.link_id,review_request.name,review_request.is_reviewed,review_request.is_pass,review_request.description,review_request_node.id as review_request_node_id,review_request_node.confirm_end_time,review_request_node.plan_reviewer')
            ->find();

        $userIdsData = [];
        if (!empty($reviewRequest)) {
            $userIdsData[$reviewRequest['plan_reviewer']] = $reviewRequest['plan_reviewer'];
        }

        // 查询用户
        $taskMemberList = $this->taskMemberModel->where(['task_id' => ['IN', implode(',', $taskIdList)]])->select();
        $userList = $partnerList = $partnerIds = $userIds = $taskPartnerIds = $taskUserIds = $taskMemberListGrouped = [];
        if (!empty($taskMemberList)) {
            $taskMemberListGrouped = array_group_by($taskMemberList, 'task_id');
            foreach ($taskMemberListGrouped as $taskId => $taskMemberList) {
                foreach ($taskMemberList as $taskMemberItem) {
                    if (!empty($taskMemberItem['partner'])) {
                        $partnerIds[] = $taskMemberItem['partner'];
                        $userIdsData[$taskMemberItem['partner']] = $taskMemberItem['partner'];
                        $taskPartnerIds[$taskId][] = $taskMemberItem['partner'];
                    }
                    if (!empty($taskMemberItem['user_id'])) {
                        $userIds[] = $taskMemberItem['user_id'];
                        $userIdsData[$taskMemberItem['user_id']] = $taskMemberItem['user_id'];
                        $taskUserIds[$taskId][] = $taskMemberItem['user_id'];
                    }
                    if (!empty($taskMemberItem['start_man_id'])) {
                        $userIdsData[$taskMemberItem['start_man_id']] = $taskMemberItem['start_man_id'];
                    }
                }
            }
        }
        if (!empty($planData['created_by'])) {
            $userIdsData[$planData['created_by']] = $planData['created_by'];
        }
        $userDataMap = [];
        if (!empty($userIdsData)) {
            $userIdsData = array_values($userIdsData);
            $userDataList = model(UserModel::class)->where(['id' => ["IN", $userIdsData]])->field("id as saas_id,union_id as id,name,avatar")->select();
            $userDataMap = array_column($userDataList, null, 'saas_id');
            foreach ($userDataList as $userItem) {
                if (in_array($userItem['saas_id'], $userIds)) {
                    $userList[] = $userItem;
                }
                if (in_array($userItem['saas_id'], $partnerIds)) {
                    $partnerList[] = $userItem;
                }
                if (!empty($reviewRequest) && ($reviewRequest['plan_reviewer'] == $userItem['saas_id'])) {
                    $reviewRequest['user'] = $userItem;
                    $reviewRequest['user']['id'] = $userItem['saas_id'];
                    unset($reviewRequest['user']['saas_id']);
                }
            }
        }

        //排期确认详情
        $planData['plan_confirm_info'] = $reviewRequest;
        $planData['user_info'] = $userList ?? [];
        $planData['partner_info'] = $partnerList ?? [];
        $planData['project_info'] = [
            'id' => $projectData['id'],
            'name' => $projectData['name']
        ];
        $planData['created_by_info'] = $userDataMap[$planData['created_by']] ?? [];
        // 查询模板待办
        $templateTodoList = DismantleTaskService::getInstance()->getDismantleTaskIds($taskIdList, TaskRelationModel::RELATION_TYPE_TEMPLATE_TODO);
        $templateTodoList = array_group_by($templateTodoList, 'source_task_id');

        // 查询模板待办
        $subTodoList = DismantleTaskService::getInstance()->getDismantleTaskIds($taskIdList, TaskRelationModel::RELATION_TYPE_SUB_TODO);
        $subTodoList = array_group_by($subTodoList, 'source_task_id');

        $taskCosMediaIdList = [];
        foreach ($taskList as &$task) {
            $taskDataJson = json_decode($task['json'] ?? '{}', true);
            $task = array_merge($task, $taskDataJson);
            unset($task['json']);
            $taskCostMediaIds = $task['task_cos_media'] ?? '';
            $task['cos_media_id_list'] = [];
            if (!empty($taskCostMediaIds)) {
                foreach (explode(',', $taskCostMediaIds) as $taskCostMediaId) {
                    $taskCosMediaIdList[] = $taskCostMediaId;
                    $task['cos_media_id_list'][] = $taskCostMediaId;
                }
            }
        }

        // 附件查询
        $cosMediaList = [];
        if (!empty($taskCosMediaIdList)) {
            $cosMediaList = model(CosMediaModel::class)->where(['id' => ['IN', $taskCosMediaIdList]])->select();
            $cosMediaList = array_map(function ($cosMediaItem) {
                $cosMediaItem['param'] = json_decode($cosMediaItem['param']);
                return $cosMediaItem;
            }, $cosMediaList);
            $cosMediaList = array_column($cosMediaList, null, 'id');
        }

        $taskTagRelationResultList = TaskRelationTagService::getInstance()->getTaskRelationListDetailList($taskIdList);

        unset($task);
        $planData['ep_list'] = [];
        if ($planData['scope'] != PlanModel::SCOPE_GLOBAL) {
            $planData['ep_id'] = PlanModel::SCOPE_GLOBAL;
        }
        foreach ($taskList as $task) {
            $task['user_info'] = [];
            $task['partner_info'] = [];
            if (!empty($taskUserIds[$task['id']])) {
                foreach ($taskUserIds[$task['id']] as $userId) {
                    if (!isset($userDataMap[$userId])) {
                        continue;
                    }
                    $task['user_info'][] = $userDataMap[$userId];
                }
            }
            if (!empty($taskPartnerIds[$task['id']])) {
                foreach ($taskPartnerIds[$task['id']] as $userId) {
                    if (!isset($userDataMap[$userId])) {
                        continue;
                    }
                    $task['partner_info'][] = $userDataMap[$userId];
                }
            }

            $task['created_by_union_id'] = $userDataMap[$task['created_by']]['id'] ?? 0;
            $task['sub_todo_ids'] = [];
            if (isset($subTodoList[$task['id']])) {
                $task['sub_todo_ids'] = array_column($subTodoList[$task['id']], 'id');
            }
            $task['template_todo_ids'] = [];
            if (isset($templateTodoList[$task['id']])) {
                $task['template_todo_ids'] = array_column($templateTodoList[$task['id']], 'id');
            }
            $task['task_cos_media'] = [];
            foreach ($task['cos_media_id_list'] as $cosMediaId) {
                if (!isset($cosMediaList[$cosMediaId])) {
                    continue;
                }
                $task['task_cos_media'][] = $cosMediaList[$cosMediaId];
            }
            $taskItemData = $task;

            $task['relate_tag'] = $taskTagRelationResult = $taskTagRelationResultList[$task['id']] ?? [];
            if (isset($taskTagRelationResult['count_module_code'])) {
                $planData['count_module_code'] = $taskTagRelationResult['count_module_code'];
            }

            if (!empty($taskTagRelationResult['result'])) {
                foreach ($taskTagRelationResult['result'] as $relationTag) {
                    foreach ($relationTag['list'] as $tag) {
                        $planData['relate_tag'][$relationTag['code']]['code'] = $relationTag['code'];
                        $planData['relate_tag'][$relationTag['code']]['data'][$tag['id']] = $tag;
                    }
                    $planData['relate_tag'][$relationTag['code']]['list'] = array_values($planData['relate_tag'][$relationTag['code']]['data']);
                    $planData['relate_tag'][$relationTag['code']]['count'] = count($planData['relate_tag'][$relationTag['code']]['list']);
                }
            }

            $planData['label'] = $taskTagRelationResult['label'];
            $planData['children_ids'] = $taskTagRelationResult['children_ids'];
            if ($planData['scope'] == PlanModel::SCOPE_SINGLE) {
                if (isset($taskTagRelationResult['episode'][0])) {
                    $planData['ep_name'] = $taskTagRelationResult['episode'][0]['code'];
                    $taskItemData['episode_name'] = $taskTagRelationResult['episode'][0]['code'];
                    $planData['ep_list'] = [$taskTagRelationResult['episode'][0]['code']];
                    $planData['ep_id'] = $taskTagRelationResult['episode'][0]['id'];
                }
            } else {
                $planData['ep_list'] = array_merge($planData['ep_list'], array_column($taskTagRelationResult['episode'], 'code'));
                $planData['ep_name'] = PlanModel::SCOPE_GLOBAL;
                $planData['ep_id'] = PlanModel::SCOPE_GLOBAL;
                $taskItemData['episode_name'] = '';
                if (!empty($taskTagRelationResult['episode'])) {
                    $taskItemData['episode_name'] = $taskTagRelationResult['episode'][0]['code'];
                }
            }
            $planData['task'][] = $taskItemData;
        }

        if (!empty($planData['relate_tag'])) {
            $planData['relate_tag'] = array_values($planData['relate_tag']);
        }
        if (!empty($planData['ep_list'])) {
            $planData['ep_count'] = count($planData['ep_list']) . "集";
        }
        return $planData;
    }

    /**
     * 获取单项目列表
     * @param $param
     * @return array|false|mixed|string
     * @throws \think\Exception
     */
    public function getPlanOneProjectSelect($param)
    {
        $filter = $param['filter'];
        $id = $filter['id'];
        // 查剧集下的计划
        $planData = $this->planModel
            ->where(['id' => $id])
            ->field('id,tenant_id,entity_id,project_id,start_time,end_time,type,task_id,scope,related_entity,is_lock')
            ->select();

        if (empty($planData)) {
            return [];
        }

        // 查询项目
        $projectId = 0;
        $taskIds = $globalPlanIds = [];
        foreach ($planData as $planDataItem) {
            if (empty($projectId)) {
                $projectId = $planDataItem['project_id'];
            }
            $taskIds[] = $planDataItem['task_id'];
            if ($planDataItem['scope'] == PlanModel::SCOPE_GLOBAL) {
                $globalPlanIds[] = $planDataItem['id'];
            }
        }

        $projectData = model(ProjectModel::class)->where(['id' => $projectId])->field('id,name')->find();

        // 查询用户
        $userList = [];
        $partnerList = [];
        $taskMemberList = $this->taskMemberModel->where(['task_id' => ['IN', $taskIds]])->select();

        $TaskUserMap = [];
        if (!empty($taskMemberList)) {
            $userIds = [];
            foreach ($taskMemberList as $taskMemberItem) {
                $userIds[$taskMemberItem['user_id']] = $taskMemberItem['user_id'];
                $TaskUserMap[$taskMemberItem['task_id']][$taskMemberItem['user_id']] = $taskMemberItem['user_id'];
            }
            $userIds = array_values($userIds);
            $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->field("id as saas_id,union_id as id,name,avatar")->select();
            $userList = array_column($userList, null, 'saas_id');
        }

        // 查询代办
        $taskData = $this->taskModel->field('id,name,plan_start_time,plan_end_time,episode_id,start_time,end_time,description')
            ->where(['id' => ["IN", $taskIds]])
            ->select();

        $taskData = array_column($taskData, null, 'id');

        $taskRelationList = model(TaskRelationTagModel::class)->where(['task_id' => ["IN", $taskIds]])->select();

        if (empty($showStepCategoryList)) {
            $showStepCategoryList = TemplateService::getInstance()->findStepCategoryShowList();
        }
        $showStepCategoryList = array_column($showStepCategoryList, null, 'id');

        $showNameChildrenMap = [];
        foreach ($showStepCategoryList as $showStepCategoryItem) {
            if (empty($showNameChildrenMap[$showStepCategoryItem['show_name']])) {
                $showNameChildrenMap[$showStepCategoryItem['show_name']] = [
                    "children_ids" => [$showStepCategoryItem['id']],
                    "module_code" => $showStepCategoryItem['module_code']
                ];
            } else {
                $showNameChildrenMap[$showStepCategoryItem['show_name']]['children_ids'][] = $showStepCategoryItem['id'];
            }
        }


        $taskLabel = [];
        $taskEpisodeIds = [];
        $episodeIds = [];
        foreach ($taskRelationList as $taskRelationItem) {
            //名字判定
            $showName = null;
            $parentName = null;
            if (isset($showStepCategoryList[$taskRelationItem['link_id']])) {
                $parentName = $showStepCategoryList[$taskRelationItem['link_id']]['show_name'];
                if ($taskRelationItem['link_module_code'] == 'show_step_category') {
                    $showName = $showStepCategoryList[$taskRelationItem['link_id']]['show_name'];
                } elseif ($taskRelationItem['link_module_code'] == 'step_category') {
                    $showName = $showStepCategoryList[$taskRelationItem['link_id']]['name'];
                }
            }

            if ($taskRelationItem['link_module_code'] == 'episode') {
                $taskEpisodeIds[$taskRelationItem['task_id']][$taskRelationItem['link_id']] = $taskRelationItem['link_id'];
                $episodeIds[$taskRelationItem['link_id']] = $taskRelationItem['link_id'];
            }

            //名字映射
            if (!empty($showName)) {
                if (!isset($taskLabel[$taskRelationItem['task_id']])) {
                    $taskLabel[$taskRelationItem['task_id']] = ['show_name' => [$showName], 'parent_name' => $parentName];
                } elseif (!in_array($showName, $taskLabel[$taskRelationItem['task_id']])) {
                    $taskLabel[$taskRelationItem['task_id']]['show_name'][] = $showName;
                }
            }
        }

        $episodeIds = array_values($episodeIds);
        $episodeList = [];
        if (!empty($episodeIds)) {
            $episodeList = model(EntityModel::class)->where(['id' => ["IN", $episodeIds]])->select();
            $episodeList = array_column($episodeList, 'code', 'id');
        }

        //获取全局排期下---剧集名列表
        $planEpisodeList = [];
        if ($globalPlanIds) {
            $planEpisodeList = $this->getPlanEpisodeList($globalPlanIds);
        }

        foreach ($planData as $planDataIndex => $planDataItem) {
            $planDataItem['label'] = $taskLabel[$planDataItem['task_id']]['show_name'] ?? [];
            if (!empty($taskLabel[$planDataItem['task_id']]['parent_name'])) {
                $currentParentName = $taskLabel[$planDataItem['task_id']]['parent_name'];
                $currentModuleCode = $showNameChildrenMap[$currentParentName]['module_code'];
                if (in_array($currentModuleCode, ['asset', 'level', 'design', 'shot', 'storyboard'])) {
                    $planDataItem['count_module_code'] = $currentModuleCode;
                }
                $planDataItem['children_ids'] = $showNameChildrenMap[$currentParentName]['children_ids'];
            }
            if ($planDataItem['scope'] == PlanModel::SCOPE_SINGLE) {
                $planDataItem['ep_name'] = $episodeList[$planDataItem['entity_id']];
                $planDataItem['ep_list'] = [$episodeList[$planDataItem['entity_id']]];
                $planDataItem['ep_id'] = $planDataItem["entity_id"];
            } else {
                //全局排期--多集情况重新获取---剧集名列表
                $planDataItem['ep_list'] = $planEpisodeList[$planDataItem['id']] ?? [];
                $planDataItem['ep_name'] = PlanModel::SCOPE_GLOBAL;
                $planDataItem['ep_id'] = PlanModel::SCOPE_GLOBAL;
            }

            if (isset($TaskUserMap[$planDataItem['task_id']])) {
                $planDataItem['user_info'] = array_values(array_intersect_key($userList, $TaskUserMap[$planDataItem['task_id']]));
            } else {
                $planDataItem['user_info'] = [];
            }
            $planDataItem['project_info'] = [
                'id' => $projectData['id'],
                'name' => $projectData['name']
            ];
            $planDataItem["task"] = $taskData[$planDataItem['task_id']] ?? [];

            $planData[$planDataIndex] = $planDataItem;
        }
        return $planData;
    }

    /**
     * 移除代办计划
     * @param $filter
     * @return array
     * @throws Exception
     * @throws \Throwable
     */
    public function removeToDoPlan($filter)
    {
        $planModel = model(PlanModel::class);
        $planList = $planModel->where($filter)->select();
        if (empty($planList)) {
            return [];
        }
        return $this->deletePlan($planList);
    }


    /**
     * 批量修改工序分类计划
     * @param $data
     * @param $userId
     * @param $tenantId
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function batchUpdateStepCategoryPlan($data, $userId, $tenantId)
    {
        // 更新排期的
        $affectedPlanIds = [];
        foreach ($data as $dataItem) {
            $planData = [
                'plan' => $dataItem['plan'],
                'task' => $dataItem['task'] ?? null,
                "executor" => $dataItem['executor'] ?? null,
                "partner" => $dataItem['partner'] ?? null,
                "relate_tag" => $dataItem['relate_tag'] ?? null,
                "plan_source" => $dataItem['plan_source'] ?? "plan",
            ];
            $result = $this->saveStepCategoryPlan($planData, $userId, $tenantId, false);
            $affectedPlanIds = array_merge($affectedPlanIds, $result['affected_plan_ids']);
        }

        return [
            'affected_plan_ids' => $affectedPlanIds
        ];
    }

    /**
     * 获取计划多项目
     * @param $param
     * @param $userId
     * @return array
     * @throws Exception
     * @throws \Exception
     */
    public function getPlanMultiProject($param, $userId)
    {
        $filter = $param['filter'];
        $stepCategoryId = $filter['step_category_id'];
        $startTime = isset($filter['start_time']) ? strtotime($filter['start_time']) : null;
        $endTime = isset($filter['end_time']) ? strtotime($filter['start_time']) : null;
        $page = $param['page'] ?? [1, C('default_page_total')];

        //查询相应项目
        $projectFilter = [
            "filter" => [
                "step_category_id" => $stepCategoryId,
            ]
        ];

        if (isset($filter["project_id"])) {
            $projectFilter["filter"]["project_id"] = $filter["project_id"];
        } else {
            if (isset($filter['is_schedule'])) {
                $projectFilter["filter"]["is_schedule"] = $filter['is_schedule'];
            }
        }


        $projectList = $this->getStepCategoryPlanProjectList($projectFilter, $userId, true);

        if (empty($projectList)) {
            return [];
        }

        $projectIds = [];
        $projectMap = [];
        foreach ($projectList as $projectItem) {
            $projectIds[] = $projectItem["id"];
            $projectMap[$projectItem["id"]] = $projectItem;
        }

        $filter["entity.project_id"] = ["IN", $projectIds];

        $episodeFilter = $filter;
        if (isset($episodeFilter["project_id"])) {
            $episodeFilter["entity.project_id"] = $episodeFilter["project_id"];
            unset($episodeFilter["project_id"]);
        }
        unset($episodeFilter['step_category_id'], $episodeFilter['start_time'], $episodeFilter['end_time'], $episodeFilter['is_schedule']);

        // 查剧集
        if (isset($filter['is_schedule'])) {
            $isSchedule = $filter['is_schedule'] === "yes" ? "COUNT(plan.id)>0" : "COUNT(plan.id)=0";
            $planScope = array_map(function ($item) {
                return "'$item'";
            }, PlanModel::TYPE_PLAN_SCOPE);

            $planScope = join(",", $planScope);
            $episodeEntityData = model(EntityModel::class)
                ->join("left join plan on plan.entity_id = entity.id and plan.type in ({$planScope}) and plan.step_category_id = {$stepCategoryId}")
                ->where(['entity.module_id' => module_by_code('episode')['id']])
                ->where($episodeFilter)
                ->field("entity.id,entity.code,entity.project_id")
                ->page($page[0], $page[1])
                ->group("entity.project_id,entity.id")
                ->having($isSchedule)
                ->order("entity.project_id asc,LENGTH(entity.code) asc,entity.code asc")
                ->select();
        } else {
            $episodeEntityData = $this->entityModel
                ->where(['module_id' => module_by_code('episode')['id']])
                ->where($episodeFilter)
                ->field('code,id,project_id')
                ->order("project_id asc,LENGTH(entity.code) asc,code asc")
                ->page($page[0], $page[1])
                ->select();
        }


        if (empty($episodeEntityData)) {
            return [
                'ep_list' => [],
                'plan_list' => []
            ];
        }

        $episodeEntityIds = [];
        $episodeEntityMap = [];
        foreach ($episodeEntityData as $index => $episodeEntityDataItem) {
            $episodeEntityDataItem["project_name"] = $projectMap[$episodeEntityDataItem["project_id"]]["name"] ?? "";
            $episodeEntityIds[] = $episodeEntityDataItem["id"];
            $episodeEntityMap[$episodeEntityDataItem["id"]] = $episodeEntityDataItem;
            $episodeEntityData[$index] = $episodeEntityDataItem;
        }

        $planFilter[] = [
            "entity_id" => ['IN', $episodeEntityIds],
            "type" => ["IN", ['step_category', 'to_do']],
            "_logic" => "AND"
        ];

        // 排期时间筛选
        $monthWhere = [];
        if (!empty($startTime) && !empty($endTime)) {
            $monthWhere[] = [
                'start_time' => ["ELT", $endTime], 'end_time' => ["EGT", $startTime]
            ];
            $monthWhere["_logic"] = "OR";
            $planFilter[] = $monthWhere;
        }

        // 环节筛选
        if (!empty($stepCategoryId)) {
            $planFilter['step_category_id'] = $stepCategoryId;
        }

        // 查剧集下的计划
        $planData = $this->planModel
            ->where($planFilter)
            ->field('id,tenant_id,step_category_id,project_id,start_time,end_time,type,entity_id,task_id')
            ->select();

        if (empty($planData)) {
            return [
                'ep_list' => $episodeEntityData,
                'plan_list' => []
            ];
        }

        $taskIds = [];
        $stepCategoryIds = [];
        foreach ($planData as $planDataItem) {
            $stepCategoryIds[$planDataItem["step_category_id"]] = $planDataItem["step_category_id"];
            if (!empty($planDataItem["task_id"])) {
                $taskIds[] = $planDataItem["task_id"];
            }
        }

        // 匹配 stepCategory
        $stepCategoryIds = array_values($stepCategoryIds);
        $stepCategoryData = $this->stepCategoryModel->where(['id' => ['IN', $stepCategoryIds]])->select();
        $stepCategoryMap = array_column($stepCategoryData, null, 'id');

        $taskMemberMap = [];
        $taskData = [];
        if (!empty($taskIds)) {
            $taskData = $this->taskModel->field('id,name,plan_start_time,plan_end_time,episode_id,start_time,end_time,description')
                ->where(['id' => ['IN', $taskIds], 'episode_id' => ['IN', $episodeEntityIds]])
                ->select();
            $taskData = array_column($taskData, null, "id");

            $taskMemberList = $this->taskMemberModel->where(['task_id' => ["IN", $taskIds]])->select();
            $userIds = array_unique(array_column($taskMemberList, 'user_id'));
            if (!empty($userIds)) {
                $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->field("id,avatar")->select();
                $userList = array_column($userList, null, 'id');
                foreach ($taskMemberList as $taskMemberItem) {
                    if (!empty($userList[$taskMemberItem['user_id']])) {
                        $taskMemberMap[$taskMemberItem['task_id']][] = $userList[$taskMemberItem['user_id']];
                    }
                }
            }
        }

        // 查询项目
        $projectIds = array_column($episodeEntityData, 'project_id');
        $projectData = model(ProjectModel::class)->where(['id' => $projectIds[0]])->field('id,name')->find();

        $planList = [];
        foreach ($planData as $item) {
            $stepCategoryId = $item["step_category_id"];
            $item['label'] = $stepCategoryMap[$stepCategoryId]['name'];
            $item['ep_name'] = $episodeEntityMap[$item['entity_id']]['code'];
            $item['ep_id'] = $item['entity_id'];
            $item['user_info'] = $taskMemberMap[$item['task_id']] ?? [];
            $item['project_info'] = [
                'id' => $projectData['id'],
                'name' => $projectData['name']
            ];
            if ($item['type'] == PlanModel::TYPE_TO_DO && isset($taskData[$item["task_id"]])) {
                $item["task"] = $taskData[$item["task_id"]];
            } else {
                $item["task"]["name"] = $stepCategoryMap[$stepCategoryId]['name'];
            }
            $planList[] = $item;
        }
        return [
            'ep_list' => $episodeEntityData,
            'plan_list' => $planList
        ];
    }

    /**
     * 获取排期详情实体列表
     * @param $param
     * @param $tenantId
     * @return array|false|mixed|string
     * @throws Exception
     */
    public function getScheduleInfoEntityList($param, $tenantId)
    {
        $statusService = StatusService::getInstance();
        $page = $param["page"] ?? [1, C("default_page_total")];
        $filter = $param["filter"];
        $projectId = $filter["project_id"];
        $taskStatus = $filter["task_status"];
        $statusFilter = $filter["status_filter"] ?? "";
        $currentMonthDeliver = $filter["current_month_deliver"];
        $stepCategoryId = $filter["step_category_id"];
        $startTime = strtotime(date("Y-m", strtotime($filter["month_time"])));
        $endTime = strtotime("+1 month", $startTime) - 1;
        $category = $filter["category"] ?? [];
        $grade = $filter["grade"] ?? [];
        $departmentId = $filter["department_id"] ?? [];
        $executor = $filter["executor"] ?? [];
        $executorTenantId = $filter["executor_tenant_id"] ?? [];
        $stepCategory = model(StepCategoryModel::class)->find($stepCategoryId);
        $parentStepCategory = model(StepCategoryModel::class)->find($stepCategory['parent_id']);
        $taskFilter = [
            "entity.project_id" => $projectId,
            "entity.module_id" => $parentStepCategory["entity_id"],
            "task.step_category_id" => $stepCategoryId,
            "task.step_type" => TaskModel::STEP_TYPE_STEP,
        ];
        if ($currentMonthDeliver == "yes") {
            $taskFilter["task.plan_end_time"] = ["BETWEEN", [$startTime, $endTime]];
        } else {
            $taskFilter["task.plan_start_time"] = ["ELT", $endTime];
            $taskFilter["task.plan_end_time"] = ["GT", $endTime];
        }

        if (!empty($category) || !empty($grade)) {
            $module = model(ModuleModel::class)->find($parentStepCategory["entity_id"]);
            $moduleCode = $module["code"];
            if (!empty($category)) {
                $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.{$moduleCode}_category'))"] = ["IN", $category];
            }
            if (!empty($grade)) {
                $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.{$moduleCode}_grade'))"] = ["IN", $grade];
            }
        }
        if (!empty($departmentId)) {
            $taskFilter["task.department_id"] = ["IN", $departmentId];
        }
        if (!empty($executor)) {
            $taskFilter["task.executor"] = ["IN", $executor];
        }
        if (!empty($executorTenantId)) {
            $taskFilter["task.executor_tenant_id"] = ["IN", $executorTenantId];
        }

        $entityQuery = model(EntityModel::class)->join("task on task.entity_id = entity.id");
        $entityFields = [
            "entity.id", "entity.initial_episode_code", "entity.showings_number", "entity.name"
        ];
        if ($taskStatus == "not_started") {
            $statusList = $statusService->getNotStartedStatus();
            $statusIds = array_column($statusList, "id");
            $taskFilter = array_merge($taskFilter, ["task.tenant_id" => $tenantId, "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => ["IN", $statusIds]]);
            switch ($statusFilter) {
                case "expired":
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
                case "unassigned":
                    $taskFilter["assign_status"] = ["IN", [TaskModel::ASSIGN_STATUS_DEFAULT]];
                    break;
                case "executor_other":
                    $entityQuery = $entityQuery->join("timelog on timelog.created_by = task.executor and timelog.complete = 'no'");
                    $taskFilter["timelog.tenant_id"] = $tenantId;
                    $taskFilter["timelog.module_id"] = module_by_code("task")["id"];
                    break;
                case "executor_idling":
                    $taskModuleId = module_by_code("task")["id"];
                    $entityQuery = $entityQuery->join("left join timelog on timelog.link_id = task.id and timelog.created_by = task.executor and timelog.module_id= {$taskModuleId}");
                    $taskFilter["task.assign_status"] = TaskModel::ASSIGN_STATUS_EXECUTOR;
                    $taskFilter["_string"] = "timelog.id is null";
                    break;
            }

        } else if ($taskStatus == "processing") {
            $statusList = $statusService->getMapBy(["correspond" => "in_progress"], "id,code", "code");
            $statusIds = array_column($statusList, "id");
            $taskFilter = array_merge($taskFilter, ["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => ["IN", $statusIds]]);
            switch ($statusFilter) {
                case "expired":
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
                case "in_progress":
                    $taskFilter[] = [
                        "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))" => "no",
                        "_string" => "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue')) IS null",
                        "_logic" => "OR"
                    ];
                    break;
                case "remake":
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = $statusList['remake']['id'];
                    break;
            }
        } else if ($taskStatus == "completed") {
            $finalStatus = $statusService->getTaskDoneStatus();
            $taskFilter = array_merge($taskFilter, ["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => $finalStatus["id"]]);
        } else if ($taskStatus == "overdue") {
            switch ($statusFilter) {
                case "not_started":
                    $statusList = $statusService->getNotStartedStatus();
                    $statusIds = array_column($statusList, "id");
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = ["IN", $statusIds];
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
                case "processing":
                    $statusList = $statusService->getMapBy(["correspond" => "in_progress"], "id,code", "code");
                    $statusIds = array_column($statusList, "id");
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = ["IN", $statusIds];
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
            }
        }
        return $entityQuery
            ->where($taskFilter)
            ->field($entityFields)
            ->group("entity.id")
            ->page(...$page)
            ->select();
    }

    /**
     * 获取排期任务列表
     * @param $param
     * @param $tenantId
     * @return array
     * @throws Exception
     */
    public function getScheduleInfoTaskList($param, $tenantId)
    {
        $filter = $param["filter"];
        $entityId = $filter["entity_id"];
        $taskStatus = $filter["task_status"];
        $statusFilter = $filter["status_filter"] ?? "";
        $currentMonthDeliver = $filter["current_month_deliver"];
        $stepCategoryId = $filter["step_category_id"];
        $startTime = strtotime(date("Y-m", strtotime($filter["month_time"])));
        $endTime = strtotime("+1 month", $startTime) - 1;
        $statusService = StatusService::getInstance();
        $taskModuleId = module_by_code("task")["id"];
        $taskQuery = model(TaskModel::class);
        $departmentId = $filter["department_id"] ?? [];
        $executor = $filter["executor"] ?? [];
        $executorTenantId = $filter["executor_tenant_id"] ?? [];
        $taskFilter = [
            "task.entity_id" => $entityId,
            "task.step_category_id" => $stepCategoryId,
            "task.step_type" => TaskModel::STEP_TYPE_STEP,
        ];
        if ($currentMonthDeliver == "yes") {
            $taskFilter["task.plan_end_time"] = ["BETWEEN", [$startTime, $endTime]];
        } else {
            $taskFilter["task.plan_start_time"] = ["ELT", $endTime];
            $taskFilter["task.plan_end_time"] = ["GT", $endTime];
        }
        $taskFields = [
            "task.id", "task.entity_id", "task.name", "task.plan_start_time", "task.plan_end_time", "task.assign_status", "task.executor", "task.department_id",
            "task.executor_tenant_id", "task.executor_tenant_id", "task.tenant_id", "task.assignee", "task.end_time", "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status",
            "task.overdue_count"
        ];

        if (!empty($departmentId)) {
            $taskFilter["task.department_id"] = ["IN", $departmentId];
        }
        if (!empty($executor)) {
            $taskFilter["task.executor"] = ["IN", $executor];
        }
        if (!empty($executorTenantId)) {
            $taskFilter["task.executor_tenant_id"] = ["IN", $executorTenantId];
        }

        if ($taskStatus == "not_started") {
            $statusList = $statusService->getNotStartedStatus();
            $statusIds = array_column($statusList, "id");
            $taskFilter = array_merge($taskFilter, ["task.tenant_id" => $tenantId, "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => ["IN", $statusIds]]);
            switch ($statusFilter) {
                case "expired":
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
                case "unassigned":
                    $taskFilter["assign_status"] = ["IN", [TaskModel::ASSIGN_STATUS_DEFAULT]];
                    break;
                case "executor_other":
                    $taskQuery = $taskQuery->join("timelog on timelog.created_by = task.executor and timelog.complete = 'no'");
                    $taskFilter["timelog.tenant_id"] = $tenantId;
                    break;
                case "executor_idling":
                    $taskQuery = $taskQuery->join("left join timelog on timelog.link_id = task.id and timelog.created_by = task.executor and timelog.tenant_id = {$tenantId} and timelog.module_id= {$taskModuleId}");
                    $taskFilter["task.assign_status"] = TaskModel::ASSIGN_STATUS_EXECUTOR;
                    $taskFilter["_string"] = "timelog.id is null";
                    break;
            }
        } else if ($taskStatus == "processing") {
            $statusList = $statusService->getMapBy(["correspond" => "in_progress"], "id,code", "code");
            $statusIds = array_column($statusList, "id");
            $taskFilter = array_merge($taskFilter, ["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => ["IN", $statusIds]]);
            switch ($statusFilter) {
                case "expired":
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
                case "in_progress":
                    $taskFilter[] = [
                        "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))" => "no",
                        "_string" => "JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue')) IS null",
                        "_logic" => "OR"
                    ];
                    break;
                case "remake":
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = $statusList['remake']['id'];
                    break;
            }
        } else if ($taskStatus == "completed") {
            $finalStatus = $statusService->getTaskDoneStatus();
            $taskFilter = array_merge($taskFilter, ["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => $finalStatus["id"]]);
        } else if ($taskStatus == "overdue") {
            switch ($statusFilter) {
                case "not_started":
                    $statusList = $statusService->getNotStartedStatus();
                    $statusIds = array_column($statusList, "id");
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = ["IN", $statusIds];
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
                case "processing":
                    $statusList = $statusService->getMapBy(["correspond" => "in_progress"], "id,code", "code");
                    $statusIds = array_column($statusList, "id");
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = ["IN", $statusIds];
                    $taskFilter["JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_is_overdue'))"] = "yes";
                    break;
            }
        }
        $taskList = $taskQuery
            ->where($taskFilter)
            ->field($taskFields)
            ->select();

        if (empty($taskList)) {
            return [];
        }

        $departmentIdMap = [];
        $executorUserId = [];
        $tenantIds = [];
        $taskIds = [];
        $versionList = [];
        $taskStatusIds = [];
        $statusList = [];
        foreach ($taskList as $taskItem) {
            if ($taskItem['department_id'] != 0 && !in_array($taskItem['department_id'], $departmentIdMap)) {
                $departmentIdMap[] = $taskItem['department_id'];
            }

            if ($taskItem['executor'] != 0 && !in_array($taskItem['executor'], $executorUserId)) {
                $executorUserId[] = $taskItem['executor'];
            }

            if ($taskItem['assignee'] != 0 && !in_array($taskItem['assignee'], $executorUserId)) {
                $executorUserId[] = $taskItem['assignee'];
            }

            if ($taskItem['executor_tenant_id'] > 0 && $taskItem['executor_tenant_id'] != $taskItem['tenant_id']) {
                $tenantIds[$taskItem['executor_tenant_id']] = $taskItem['executor_tenant_id'];
            } else {
                $tenantIds[$taskItem['tenant_id']] = $taskItem['tenant_id'];
            }
            $taskIds[] = $taskItem['id'];
            $taskStatusIds[] = $taskItem['task_status'];
        }


        $userInfo = TaskService::getInstance()->queryTaskSupplierInfo($tenantId, [], [], $departmentIdMap, $executorUserId, array_values($tenantIds));
        $executorList = $userInfo["executor"];
        $departmentList = $userInfo["department"];
        $tenantList = $userInfo["tenant"];

        $statusList = model(StatusModel::class)->field("id,name,code,color,icon")->where(['id' => ['IN', $taskStatusIds]])->select();
        $statusList = array_column($statusList, null, "id");

        if ($taskStatus == "processing" || $taskStatus == "completed" || $taskStatus == "overdue") {
            $versionList = model(VersionModel::class)->field("link_id,max(number) as number")->where(['link_id' => ['IN', $taskIds], 'module_id' => $taskModuleId])->group("link_id")->select();
            if ($versionList) {
                $versionList = array_column($versionList, null, 'link_id');
            }
        }

        $currentTime = time();
        $taskDealFunction = function ($taskItem) use ($executorList, $departmentList, $tenantList, $currentTime, $versionList, $statusList) {
            $taskItem["executor"] = $executorList[$taskItem["executor"]] ?? [];
            $taskItem["assignee"] = $executorList[$taskItem["assignee"]] ?? [];
            $taskItem["department"] = $departmentList[$taskItem["department_id"]] ?? [];
            $currentTenant = $taskItem['tenant_id'];
            if ($taskItem['executor_tenant_id'] > 0 && $taskItem['executor_tenant_id'] != $taskItem['tenant_id']) {
                $currentTenant = $taskItem['executor_tenant_id'];
            }

            //逾期总时长计算
            $taskItem["overdue_duration"] = 0;
            if ($taskItem['plan_end_time'] > 0) {
                if ($taskItem["end_time"] > 0) { //判定是否完成
                    if ($taskItem["end_time"] > $taskItem['plan_end_time']) {
                        $taskItem["overdue_duration"] = $taskItem["end_time"] - $taskItem['plan_end_time'];
                    }
                } else {
                    if ($currentTime > $taskItem['plan_end_time']) {
                        $taskItem["overdue_duration"] = $currentTime - $taskItem['plan_end_time'];
                    }
                }
            }
            //TODO 逾期次数的计算暂时默认为1
            if ($taskItem["overdue_duration"] > 0) {
                //换算为小时
                $taskItem["overdue_duration"] = round($taskItem["overdue_duration"] / (60 * 60), 2);
            }

            $taskItem['tenant_info'] = $tenantList[$currentTenant] ?? null;
            $taskItem['remake_count'] = 0;
            if (!empty($versionList[$taskItem['id']])) {
                $taskItem['remake_count'] = $versionList[$taskItem['id']]['number'] - 1;
            }
            if (!empty($statusList[$taskItem['task_status']])) {
                $taskItem['status'] = $statusList[$taskItem['task_status']];
            }
            return $taskItem;
        };

        $taskNextDealFunction = $taskDealFunction;
        if ($taskStatus == "not_started") {
            switch ($statusFilter) {
                case "executor_other":
                    //查询当前公司执行人正在执行的任务
                    $timeLogList = model(TimelogModel::class)->where([
                        "tenant_id" => $tenantId,
                        "module_id" => module_by_code("task")["id"],
                        "created_by" => ["IN", $executorUserId],
                        "complete" => "no"
                    ])->field("created_by,link_id")->select();

                    $timeLogMap = [];
                    $taskIds = [];
                    foreach ($timeLogList as $timeLogItem) {
                        $timeLogMap[$timeLogItem["created_by"]] = $timeLogItem;
                        $taskIds[] = $timeLogItem["link_id"];
                    }

                    $executorTaskFields = ["task.id", "entity.initial_episode_code", "entity.showings_number", "entity.name", "task.name as task_name"];
                    $executorTaskList = model(TaskModel::class)->join("entity on entity.id = task.entity_id")
                        ->where(["task.id" => ["IN", $taskIds]])->field($executorTaskFields)->select();
                    $executorTaskList = array_column($executorTaskList, null, "id");

                    $taskDealFunction = function ($taskItem) use (&$taskNextDealFunction, &$timeLogMap, &$executorTaskList) {
                        if (isset($timeLogMap[$taskItem["executor"]])) {
                            if (isset($executorTaskList[$timeLogMap[$taskItem["executor"]]["link_id"]])) {
                                $taskItem["executor_task"] = $executorTaskList[$timeLogMap[$taskItem["executor"]]["link_id"]];
                            }
                        }
                        return $taskNextDealFunction($taskItem);
                    };
                    break;
                case "executor_idling":
                    //上游查找
                    $executorIdlingTaskIds = array_column($taskList, "id");
                    $upTaskFields = ["id", "name", "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status"];
                    $taskUpTaskMap = TaskUpstreamService::getInstance()->batchGetUpstreamTaskList($executorIdlingTaskIds, $upTaskFields);
                    //先判定上游和关联是否未完成, 再判定执行人是否未开始
                    $doneStatus = StatusService::getInstance()->getTaskDoneStatus();
                    //过滤掉已完成的
                    $upEntityIds = [];
                    $upStatusIds = [];
                    foreach ($taskUpTaskMap as $taskIdIndex => $taskUpTaskMapList) {
                        foreach ($taskUpTaskMapList as $taskUpTaskMapIndex => $taskUpTaskMapItem) {
                            if (intval($taskUpTaskMapItem["task_status"]) === $doneStatus["id"]) {
                                unset($taskUpTaskMapList[$taskUpTaskMapIndex]);
                            }
                            $upEntityIds[] = $taskUpTaskMapItem["entity_id"];
                            $upStatusIds[] = $taskUpTaskMapItem["task_status"];
                        }
                        $taskUpTaskMap[$taskIdIndex] = $taskUpTaskMapList;
                    }

                    $upStatusList = [];
                    if (!empty($upStatusIds)) {
                        $upStatusList = StatusService::getInstance()->getList($upStatusIds, "id,name,code");
                        $upStatusList = array_column($upStatusList, null, "id");
                    }

                    $upEntityList = [];
                    if (!empty($upEntityIds)) {
                        $upEntityList = model(EntityModel::class)->where(["id" => ["IN", $upEntityIds]])->field("id,initial_episode_code,showings_number,name")->select();
                        $upEntityList = array_column($upEntityList, null, "id");
                    }

                    $taskDealFunction = function ($taskItem) use (&$taskNextDealFunction, &$taskUpTaskMap, $upEntityList, $upStatusList) {
                        $taskItem["is_exist_up"] = !empty($taskUpTaskMap[$taskItem["id"]]);

                        $taskItem["idling_reason"] = [];
                        if (isset($taskUpTaskMap[$taskItem["id"]])) {
                            foreach ($taskUpTaskMap[$taskItem["id"]] as $taskUpTaskMapIndex => $taskUpTaskMapItem) {
                                $taskUpTaskMapItem = [
                                    "is_relation_up" => $taskUpTaskMapItem["entity_id"] === $taskItem["entity_id"] ? "relation" : "up",
                                    "task" => $taskUpTaskMapItem,
                                    "entity" => $upEntityList[$taskUpTaskMapItem["entity_id"]] ?? [],
                                    "status" => $upStatusList[$taskUpTaskMapItem["task_status"]]
                                ];
                                $taskUpTaskMap[$taskItem["id"]][$taskUpTaskMapIndex] = $taskUpTaskMapItem;
                            }
                            $taskItem["idling_reason"] = $taskUpTaskMap[$taskItem["id"]];
                        }
                        return $taskNextDealFunction($taskItem);
                    };
                    break;
            }
        }

        return array_map($taskDealFunction, $taskList);
    }

    /**
     * 计划指定集复制
     * @param $planIds
     * @param $episodeId
     * @param $userId
     * @param $tenantId
     * @return array
     * @throws \Throwable
     */
    public function planSpecifyEpisodeCopy($planIds, $episodeId, $userId, $tenantId)
    {
        $planList = model(PlanModel::class)
            ->where(['id' => ["IN", $planIds]])
            ->field("id,entity_id,type,start_time,end_time,task_id,project_id,scope")
            ->select();


        $taskPlanMap = [];
        // 获取该集下所有的计划
        $projectId = 0;

        $planTaskIds = [];
        $toDoPlanTaskIds = [];
        $episodeLimit = [];
        foreach ($planList as $planItem) {
            // 验证 不同集的不能复制 , 全局的不能复制
            if ($planItem['scope'] === PlanModel::SCOPE_GLOBAL) {
                throw new LogicException("scope plan can not be copy", ErrorCode::SCOPE_PLAN_CAN_NOT_BE_COPY);
            }

            if (!in_array($planItem['entity_id'], $episodeLimit)) {
                $episodeLimit[] = $planItem['entity_id'];
            }
            if (count($episodeLimit) > 1) {
                throw new LogicException("plan must same episode", ErrorCode::PLAN_MUST_SAME_EPISODE);
            }

            if (in_array($planItem['type'], PlanModel::TYPE_TODO_GROUP)) {
                $toDoPlanList[] = $planItem;
                $toDoPlanTaskIds[] = $planItem['task_id'];
            } else {
                $planTaskIds[] = $planItem['task_id'];
            }
            if (empty($projectId)) {
                $projectId = $planItem['project_id'];
            }
            $taskPlanMap[$planItem['task_id']] = $planItem;
        }

        //获取指定集下的工序分类计划
        $specifyPlan = model(PlanModel::class)
            ->where([
                'plan.project_id' => $projectId,
                'plan.entity_id' => $episodeId,
                'plan.scope' => PlanModel::SCOPE_SINGLE,
                'plan.type' => PlanModel::TYPE_STEP_CATEGORY
            ])->field("id,entity_id,type,start_time,end_time,task_id,project_id,scope")->select();

        $specifyPlanTaskIds = [];

        foreach ($specifyPlan as $specifyPlanItem) {
            $specifyPlanTaskIds[] = $specifyPlanItem['task_id'];
            $taskPlanMap[$specifyPlanItem['task_id']] = $specifyPlanItem;
        }

        $mergeTaskIds = array_merge($planTaskIds, $specifyPlanTaskIds);
        $mergeToDoTaskIds = array_merge($mergeTaskIds, $toDoPlanTaskIds);

        //比较工序分类是否相同
        $taskRelationTagList = model(TaskRelationTagModel::class)->where([
            'task_id' => ["IN", $mergeToDoTaskIds],
//            "link_module_code" => ["IN", ["show_step_category", "step_category"]]
        ])->field('task_id,link_module_code,link_id')->select();

        $taskRelationTagMap = [];
        $taskRelationTagGroupMap = [];
        foreach ($taskRelationTagList as $taskRelationTagItem) {
            if (in_array($taskRelationTagItem['task_id'], $mergeTaskIds)) {
                if (in_array($taskRelationTagItem['link_module_code'], ["show_step_category", "step_category"])) {
                    if (!isset($taskRelationTagMap[$taskRelationTagItem['task_id']])) {
                        $taskRelationTagMap[$taskRelationTagItem['task_id']] = [$taskRelationTagItem['link_id']];
                    } else {
                        $taskRelationTagMap[$taskRelationTagItem['task_id']][] = $taskRelationTagItem['link_id'];
                    }
                }
            }
            $taskRelationTagGroupMap[$taskRelationTagItem['task_id']][] = $taskRelationTagItem;
        }


        $modifyPlanTaskIds = [];
        $taskIds = [];
        foreach ($planTaskIds as $planTaskId) {
            $isInsert = true;
            foreach ($specifyPlanTaskIds as $specifyPlanTaskId) {
                // 判定是否有交集 , 在判定是否有差集
                $isIntersect = array_intersect($taskRelationTagMap[$planTaskId] ?? [], $taskRelationTagMap[$specifyPlanTaskId] ?? []);
                if (!empty($isIntersect)) {
                    $isDiff = array_diff($taskRelationTagMap[$planTaskId] ?? [], $taskRelationTagMap[$specifyPlanTaskId] ?? []);
                    if (!empty($isDiff)) {
                        throw new LogicException("step_category plan not same", ErrorCode::PLAN_VALIDATE_FAILURE);
                    } else {
                        $modifyPlanTaskIds[$planTaskId] = $specifyPlanTaskId;
                        $taskIds[] = $planTaskId;
                        $isInsert = false;
                    }
                }
            }

            if ($isInsert) {
                $taskIds[] = $planTaskId;
            }
        }
        //如果不抛错误,则直接覆盖或者添加新的值
        $allPlanTaskIds = array_merge($taskIds, $toDoPlanTaskIds);


        $result = [];
        if (!empty($allPlanTaskIds)) {
            $allPlanTaskList = model(TaskModel::class)->where([
                'id' => ["IN", $allPlanTaskIds]
            ])->field('id,name,description,plan_start_time,plan_end_time,step_type')->select();

            if (!empty($allPlanTaskList)) {
                $allPlanTaskMemberList = model(TaskMemberModel::class)
                    ->where([
                        'task_id' => ["IN", $allPlanTaskIds]
                    ])->select();


                $taskExecutor = [];
                $taskPartner = [];
                $userIds = [];
                foreach ($allPlanTaskMemberList as $taskMemberItem) {
                    if (!empty($taskMemberItem['user_id'])) {
                        $userIds[$taskMemberItem['user_id']] = $taskMemberItem['user_id'];
                    }

                    if (!empty($taskMemberItem['partner'])) {
                        $userIds[$taskMemberItem['partner']] = $taskMemberItem['partner'];
                    }
                }

                if (!empty($userIds)) {
                    $userIds = array_values($userIds);
                    $userList = model(UserModel::class)->where(['id' => ["IN", $userIds]])->select();
                    $userList = array_column($userList, 'union_id', 'id');
                    foreach ($allPlanTaskMemberList as $taskMemberItem) {
                        if (!empty($taskMemberItem['user_id']) && isset($userList[$taskMemberItem['user_id']])) {
                            $taskExecutor[$taskMemberItem['task_id']][] = $userList[$taskMemberItem['user_id']];
                        }
                        if (!empty($taskMemberItem['partner']) && isset($userList[$taskMemberItem['partner']])) {
                            $taskPartner[$taskMemberItem['task_id']][] = $userList[$taskMemberItem['partner']];
                        }
                    }
                }

                //环节制作的数据
                foreach ($allPlanTaskList as $allPlanTaskItem) {
                    // 从赋值方拿取标签 , 该标签要更改覆盖方的标签中的集id ,去除标签中的任务id
                    if (isset($taskRelationTagGroupMap[$allPlanTaskItem['id']])) {
                        foreach ($taskRelationTagGroupMap[$allPlanTaskItem['id']] as &$taskRelationTagGroupMapItem) {
                            unset($taskRelationTagGroupMapItem['task_id']);
                            if ($taskRelationTagGroupMapItem['link_module_code'] == 'episode') {
                                $taskRelationTagGroupMapItem['link_id'] = $episodeId;
                            }
                        }
                    }

                    // 如果是修改的直接更改id为 覆盖方的id ,新增则直接去除掉排期id,task_id,task.id
                    $currentPlan = $taskPlanMap[$allPlanTaskItem['id']];
                    $executor = $taskExecutor[$allPlanTaskItem['id']] ?? [];
                    $partner = $taskPartner[$allPlanTaskItem['id']] ?? [];

                    if (isset($modifyPlanTaskIds[$allPlanTaskItem['id']])) {
                        $currentPlan['id'] = $taskPlanMap[$modifyPlanTaskIds[$allPlanTaskItem['id']]]['id'];
                        $currentPlan['task_id'] = $modifyPlanTaskIds[$allPlanTaskItem['id']];
                        $allPlanTaskItem['id'] = $modifyPlanTaskIds[$allPlanTaskItem['id']];
                        $allPlanTaskId = $allPlanTaskItem['id'];
                    } else {
                        $allPlanTaskId = $allPlanTaskItem['id'];
                        unset($currentPlan['id'], $currentPlan['task_id'], $allPlanTaskItem['id']);
                    }

                    $currentPlan['start_time'] = date('Y-m-d H:i:s', $currentPlan['start_time']);
                    $currentPlan['end_time'] = date('Y-m-d H:i:s', $currentPlan['end_time']);
                    $allPlanTaskItem['plan_start_time'] = date('Y-m-d H:i:s', $allPlanTaskItem['plan_start_time']);
                    $allPlanTaskItem['plan_end_time'] = date('Y-m-d H:i:s', $allPlanTaskItem['plan_end_time']);

                    $saveStepCategoryPlanData = [
                        'plan' => $currentPlan,
                        'task' => $allPlanTaskItem,
                        'relate_tag' => $taskRelationTagGroupMap[$allPlanTaskId],
                        'executor' => $executor,
                        'partner' => $partner,
                    ];
                    $planData = $this->saveStepCategoryPlan($saveStepCategoryPlanData, $userId, $tenantId, false);
                    $result[] = $planData["plan"]['id'];
                }
            }
        }

        return $result;
    }

    /**
     * 限制验证
     * @param $param
     * @param $producerStepCategoryList
     * @param bool $isValidatePlan
     * @return array
     */
    public function validatePlan(&$param, $producerStepCategoryList, $isValidatePlan = true)
    {
        //单集应该只能存在一集 的标签
        $episodeCount = 0;
        $isShowStepCategory = false;
        $isStepCategory = false;
        $isScene = false;

        // 查询当前项目下的模板显示的环节

        if (empty($producerStepCategoryList)) {
            return [];
        }

        $showStepCategoryIdProjectList = [];
        $showStepCategoryIdProjectMap = [];
        foreach ($producerStepCategoryList as $item) {
            $showStepCategoryIdProjectIds[] = $item['id'];
            $showStepCategoryIdProjectList[$item['show_name']][] = $item;
            $showStepCategoryIdProjectMap[$item['id']] = $item['module_code'];
            $showStepCategoryIdProjectMap[$item['show_name']] = $item['module_code'];
        }


        $tagType = [];
        $showStepCategoryLinkIds = $episodeIds = [];

        //是否是全局计划
        $isGlobalStepCategoryPlan = $param['plan']['type'] == PlanModel::TYPE_STEP_CATEGORY && $param['plan']['scope'] == PlanModel::SCOPE_GLOBAL;
        //全局环节计划 特定标签通行 如果标签中存在一下模块的环节且有存在一下环节的实体 module_code_step_category && module_entity
        $specificModuleScope = ['asset' => [false, false], 'level' => [false, false], 'design' => [false, false]];

        if (!empty($param['relate_tag'])) {
            foreach ($param['relate_tag'] as $dataRelateTagIndex => $dataRelateTagItem) {
                if ($dataRelateTagItem['link_module_code'] == "episode") {
                    ++$episodeCount;
                    $episodeIds[] = $dataRelateTagItem['link_id'];
                }

                if ($dataRelateTagItem['link_module_code'] == "show_step_category") {
                    $isShowStepCategory = true;

                    if (isset($showStepCategoryIdProjectMap[$dataRelateTagItem['link_id']]) && isset($specificModuleScope[$showStepCategoryIdProjectMap[$dataRelateTagItem['link_id']]])) {
                        $specificModuleScope[$showStepCategoryIdProjectMap[$dataRelateTagItem['link_id']]][1] = true;
                    }

                    if ($isValidatePlan) {
                        $showStepCategoryLinkIds[] = $dataRelateTagItem['link_id'];
                        unset($param['relate_tag'][$dataRelateTagIndex]);
                    }
                }

                if ($dataRelateTagItem['link_module_code'] == "scene") {
                    $isScene = true;
                }

                // 全局特定标签通行
                if ($isGlobalStepCategoryPlan && isset($specificModuleScope[$dataRelateTagItem['link_module_code']])) {
                    $specificModuleScope[$dataRelateTagItem['link_module_code']][0] = true;
                }

                if ($dataRelateTagItem['link_module_code'] == "step_category") {
                    $showStepCategoryLinkIds[] = $dataRelateTagItem['link_id'];
                    unset($param['relate_tag'][$dataRelateTagIndex]);
                    // 全局特定标签通行
                    if ($isGlobalStepCategoryPlan && isset($showStepCategoryIdProjectMap[$dataRelateTagItem['link_id']]) && isset($specificModuleScope[$showStepCategoryIdProjectMap[$dataRelateTagItem['link_id']]])) {
                        $specificModuleScope[$showStepCategoryIdProjectMap[$dataRelateTagItem['link_id']]][1] = true;
                    }

                    $isStepCategory = true;
                }

                $tagType[$dataRelateTagItem['link_module_code']] = $dataRelateTagItem['link_module_code'];
            }

            $tagCountType = count($tagType);

            //单集的计划 集只能关联一集
            if ($param['plan']['scope'] == PlanModel::SCOPE_SINGLE && $episodeCount != 1) {
                throw new LogicException("plan only relate one episode", ErrorCode::PLAN_ONLY_RELATE_ONE_EPISODE);
            }

            //特定环节限定
            $specificStepCategoryLimit = false;

            if ($isGlobalStepCategoryPlan) { // 是否是全局排期
                //判断全局排期--剧集是否已有排期
                $attribute = $param['plan']['attribute'] ??'';
                $this->checkGlobalPlanEpisode($param['plan']['project_id'], $showStepCategoryLinkIds[0], $episodeIds, $attribute);

                foreach ($specificModuleScope as $specificModuleScopeItem) {
                    // 全局特定标签通行
                    if (!$specificStepCategoryLimit) {
                        $specificStepCategoryLimit = $specificModuleScopeItem[0] && $specificModuleScopeItem[1];
                    }
                }

                if (!($isShowStepCategory || $isStepCategory) ||  // 如果不存在工序
                    !($episodeCount > 0 || $isScene || $specificStepCategoryLimit)  // 如果集或者场次都不存在或者存在额外限定
                ) {
                    throw new LogicException("step category global plan must relation episode or scene or specific step category entity and step category tag",
                        ErrorCode::STEP_CATEGORY_GLOBAL_PLAN_MUST_RELATION_TAG);
                }
            }


            //大分类与小分类不能同时存在
            if ($isShowStepCategory && $isStepCategory) {
                throw new LogicException("show_step_category and step_category can not exist at the same time", ErrorCode::SHOW_STEP_CATEGORY_AND_STEP_CATEGORY_CAN_NOT_EXIST_AT_THE_SAME_TIME);
            }

            // 如果有环节工序分类 最多只能存在两个标签 ,如果没有环节工序分类 最多只能存在一个标签
            if ($isShowStepCategory || $isStepCategory) {
                if ($tagCountType > 2) {
                    throw new LogicException("plan tag no more than two limit", ErrorCode::PLAN_TAG_NO_MORE_THAN_LIMIT);
                }
            } elseif ($tagCountType > 1) {
                throw new LogicException("plan tag no more than one limit", ErrorCode::PLAN_TAG_NO_MORE_THAN_LIMIT);
            }

            foreach ($showStepCategoryLinkIds as $showStepCategoryLinkId) {
                $linkModuleCode = 'show_step_category';
                if ($isStepCategory) {
                    $linkModuleCode = 'step_category';
                }
                //团队--环节ID
                if (is_numeric($showStepCategoryLinkId)) {
                    //获取团队环节配制
                    $teamInfo = ProjectTeamService::getInstance()->getTeamConfig($showStepCategoryLinkId);
                    if (empty($teamInfo['list'][0]['step_category_id'])) {
                        throw new LogicException("show step category name not exist", ErrorCode::SHOW_STEP_CATEGORY_NAME_NOT_EXIST);
                    }
                    //需要将其更新为大分类标签
                    foreach ($teamInfo['list'][0]['step_category_id'] as $showNameItem) {
                        $param['relate_tag'][] = [
                            'link_module_code' => $linkModuleCode,
                            'link_id' => $showNameItem
                        ];
                    }
                } else {
                    if (empty($showStepCategoryIdProjectList[$showStepCategoryLinkId])) {
                        throw new LogicException("show step category name not exist", ErrorCode::SHOW_STEP_CATEGORY_NAME_NOT_EXIST);
                    }
                    //需要将其更新为大分类标签
                    foreach ($showStepCategoryIdProjectList[$showStepCategoryLinkId] as $showNameItem) {
                        $param['relate_tag'][] = [
                            'link_module_code' => $linkModuleCode,
                            'link_id' => $showNameItem['id']
                        ];
                    }
                }
            }
        }
    }


    /**
     * 判断全局排期--剧集是否已有排期
     * @param int   $projectId 项目ID
     * @param int   $teamStepCategoryId 团队环节ID
     * @param array $episodeIds 剧集Ids
     * @return void
     */
    public function checkGlobalPlanEpisode(int $projectId, int $teamStepCategoryId, array $episodeIds, string $attribute='')
    {
        //获取团队环节配制
        $teamInfo = ProjectTeamService::getInstance()->getTeamConfig($teamStepCategoryId);
        if (empty($teamInfo['list'][0]['step_category_id'][0])) {
            return ;
        }

        $condition = [
            'tag.link_id' => $teamInfo['list'][0]['step_category_id'][0],
            'plan.project_id' => $projectId,
            'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
            'plan.attribute' => $attribute,
            'plan.scope' => PlanModel::SCOPE_GLOBAL
        ];
        //查询这个环节有么有--全局排期
        $planIds = model(PlanModel::class)
            ->join("task_relation_tag tag on tag.task_id = plan.task_id and tag.link_module_code in ('step_category','show_step_category')")
            ->where($condition)
            ->field("plan.id")
            ->column('id');
        if (empty($planIds)) {
            return ;
        }
        //查询排期关联的任务ID
        $planTaskIds = model(PlanTaskRelationModel::class)->field('task_id')->where(['plan_id' => ['in', $planIds]])->column('task_id');
        //返回剧集名
        $hasOne = model(TaskRelationTagModel::class)
            ->where(['task_id' => ['IN', $planTaskIds], 'link_id' => ['IN', $episodeIds], 'link_module_code' => 'episode'])
            ->field('id')
            ->find();

        if ($hasOne) {
            throw new LogicException("The episodes already has a schedule", ErrorCode::EPISODES_ALREADY_HAS_PLAN);
        }

    }

    /**
     * 获取验证结果
     * @param $planIds
     * @param $episodeId
     * @return array[]
     */
    public function getValidatePlanResult($planIds, $episodeId)
    {
        // 只有单集的排期才有限制
        $planList = model(PlanModel::class)
            ->join('task on task.id = plan.task_id')
            ->where(['plan.id' => ["IN", $planIds], 'plan.type' => PlanModel::TYPE_STEP_CATEGORY, 'plan.scope' => PlanModel::SCOPE_SINGLE])
            ->field("plan.id,plan.entity_id,plan.type,plan.start_time,plan.end_time,plan.task_id,plan.project_id,scope,task.name")
            ->select();

        if (empty($planList)) {
            return ['not_exactly_same_plan' => [], 'complete_coverage_plan' => []];
        }

        $episode = model(EntityModel::class)->field("JSON_UNQUOTE(JSON_EXTRACT(json,'$.episode_plan_seal_time')) as episode_plan_seal_time")->find($episodeId);

        // todo :不能复制不同集的
        // 获取该集下所有的计划
        $projectId = 0;

        $planTaskIds = [];
        $moreThan = [];
        foreach ($planList as $planItem) {
            if (empty($projectId)) {
                $projectId = $planItem['project_id'];
            }

            if (!empty($episode['episode_plan_seal_time']) && $planItem['end_time'] > $episode['episode_plan_seal_time']) {
                $moreThan[$planItem['task_id']] = $planItem['task_id'];
            }

            $planTaskIds[] = $planItem['task_id'];
        }

        //获取指定集下的工序分类计划
        $specifyPlan = model(PlanModel::class)
            ->where([
                'plan.project_id' => $projectId,
                'plan.entity_id' => $episodeId,
                'plan.scope' => PlanModel::SCOPE_SINGLE,
                'plan.type' => PlanModel::TYPE_STEP_CATEGORY
            ])->field("id,entity_id,type,start_time,end_time,task_id,project_id,scope")->select();

        $specifyPlanTaskIds = [];
        foreach ($specifyPlan as $specifyPlanItem) {
            $specifyPlanTaskIds[] = $specifyPlanItem['task_id'];
        }

        $mergeTaskIds = array_merge($planTaskIds, $specifyPlanTaskIds);

        //比较工序分类是否相同
        $taskRelationTagList = model(TaskRelationTagModel::class)->where([
            'task_id' => ["IN", $mergeTaskIds],
            "link_module_code" => ["IN", ["show_step_category", "step_category"]]
        ])->select();

        $producerStepCategoryList = TemplateService::getInstance()->findStatisticalStepTemplate();

        $taskRelationTagMap = [];
        $taskLabelMap = [];
        foreach ($taskRelationTagList as $taskRelationTagItem) {

            if (!isset($taskRelationTagMap[$taskRelationTagItem['task_id']])) {
                $taskRelationTagMap[$taskRelationTagItem['task_id']] = [$taskRelationTagItem['link_id']];
            } else {
                $taskRelationTagMap[$taskRelationTagItem['task_id']][] = $taskRelationTagItem['link_id'];
            }

            if (isset($producerStepCategoryList[$taskRelationTagItem['link_id']])) {
                if ($taskRelationTagItem['link_module_code'] == 'show_step_category') {
                    $showName = $producerStepCategoryList[$taskRelationTagItem['link_id']]['show_name'];
                } else {
                    $showName = $producerStepCategoryList[$taskRelationTagItem['link_id']]['name'];
                }

                if (!isset($taskLabelMap[$taskRelationTagItem['task_id']])) {
                    $taskLabelMap[$taskRelationTagItem['task_id']] = [
                        $showName
                    ];
                } elseif (!in_array($showName, $taskLabelMap[$taskRelationTagItem['task_id']])) {
                    $taskLabelMap[$taskRelationTagItem['task_id']][] = $showName;
                }
            }
        }

        // 比较 任务之间 标签是否不完全相同
        $notExactlySamePlanTaskId = [];
        //可完全覆盖任务id
        $completeCoveragePlanTaskId = [];
        foreach ($planTaskIds as $planTaskId) {
            foreach ($specifyPlanTaskIds as $specifyPlanTaskIndex => $specifyPlanTaskId) {
                // 判定是否有交集 , 在判定是否有差集
                $targetRelationTag = $taskRelationTagMap[$planTaskId] ?? [];
                $specifyRelationTag = $taskRelationTagMap[$specifyPlanTaskId] ?? [];
                $isIntersect = array_intersect($targetRelationTag, $specifyRelationTag);
                if (!empty($isIntersect)) {
                    $isDiff = !empty(array_diff($targetRelationTag, $specifyRelationTag)) || !empty(array_diff($specifyRelationTag, $targetRelationTag));
                    if ($isDiff) {
                        $notExactlySamePlanTaskId[$planTaskId] = $planTaskId;

                        unset($specifyPlanTaskIds[$specifyPlanTaskIndex]);
                    } else {
                        $completeCoveragePlanTaskId[$planTaskId] = $planTaskId;
                    }
                }
            }
            if (empty($specifyPlanTaskIds)) {
                break;
            }
        }

        // 去除掉完全覆盖中的 含有不完全相同的
        $completeCoveragePlanTaskId = array_diff_key($completeCoveragePlanTaskId, $notExactlySamePlanTaskId, $moreThan);

        $notExactlySamePlanData = [];
        $completeCoveragePlanData = [];
        foreach ($planList as $specifyPlanItem) {
            $specifyPlanItem['label'] = $taskLabelMap[$specifyPlanItem['task_id']] ?? [];
            if (isset($notExactlySamePlanTaskId[$specifyPlanItem['task_id']])) {
                $specifyPlanItem['reason'] = ['存在不相同的工序分类'];
                $notExactlySamePlanData[$specifyPlanItem['id']] = $specifyPlanItem;
            }
            if (isset($moreThan[$specifyPlanItem['task_id']])) {
                $specifyPlanItem['reason'][] = '超出封板日期';
                $notExactlySamePlanData[$specifyPlanItem['id']] = $specifyPlanItem;
            }

            if (isset($completeCoveragePlanTaskId[$specifyPlanItem['task_id']])) {
                $completeCoveragePlanData[] = $specifyPlanItem;
            }
        }


        return [
            'not_exactly_same_plan' => array_values($notExactlySamePlanData),
            'complete_coverage_plan' => $completeCoveragePlanData,
        ];
    }

    /**
     * 检测是否能完全覆盖
     * @param $planTaskIds
     * @param $relateTagLinkIds
     * @return int|mixed
     */
    public function detectionCompleteCoverage($planTaskIds, $relateTagLinkIds)
    {
        // 1. 获取 所需对比的计划的 标签
        // 2. 判定是否有交集 ,如有交集判定是否完全相同,如果有可完全覆盖 , 如果没有可以直接添加
        if (empty($planTaskIds)) {
            return $planTaskIds;
        }

        //比较工序分类是否相同
        $taskRelationTagList = model(TaskRelationTagModel::class)->where([
            'task_id' => ["IN", $planTaskIds],
            "link_module_code" => ["IN", ["show_step_category", "step_category"]]
        ])->select();

        // 获取存在相关的标签且又不完全相同的
        $taskTagMap = [];
        $tagTaskIds = [];
        foreach ($taskRelationTagList as $taskRelationTagItem) {
            //找出有交集的相关的任务id
            if (in_array($taskRelationTagItem['link_id'], $relateTagLinkIds) && !in_array($taskRelationTagItem['task_id'], $tagTaskIds)) {
                $tagTaskIds[] = $taskRelationTagItem['task_id'];
            }
            // 建立映射 task_id -> [tag_id]
            $taskTagMap[$taskRelationTagItem['task_id']][] = $taskRelationTagItem['link_id'];
        }

        $exactlySameTaskId = 0;
        // 与有交集的任务进行比较 ,
        foreach ($tagTaskIds as $tagTaskId) {
            //获取其环节工序分类
            $currentTag = $taskTagMap[$tagTaskId];
            $diffTag = !empty(array_diff($relateTagLinkIds, $currentTag)) || !empty(array_diff($currentTag, $relateTagLinkIds));
            //如果不相同 则 抛出异常 ,相同则进行内容覆盖
            if ($diffTag) {
                throw new LogicException("step_category plan not same", ErrorCode::PLAN_VALIDATE_FAILURE);
            } else {
                // 找到能直接覆盖的任务id
                $exactlySameTaskId = $tagTaskId;
            }
        }

        // 如果为0 则证明没有交集,直接通过, 可直接添加
        return $exactlySameTaskId;
    }

    /**
     * 删除排期
     * @param $planList
     * @return array
     * @throws \Throwable
     */
    public function deletePlan($planList)
    {
        $planIds = array_column($planList, 'id');
        $result = [];
        $projectId = [];
        $planTaskRelationList = model(PlanTaskRelationModel::class)->where(['plan_id' => ['IN', implode(',', $planIds)]])->select();
        $taskIds = array_column($planTaskRelationList, 'task_id');

        foreach ($planList as $planItem) {
            if (empty($projectId)) {
                $projectId = $planItem['project_id'];
            }
        }
        if (empty($planIds)) {
            return [];
        }

        //存在排期确认的不能删除
        $reviewRequest = model(ReviewRequestModel::class)
            ->where(['project_id' => $projectId, 'module_id' => module_by_code('plan')['id'], 'link_id' => ['IN', $planIds]])
            ->find();

        if (!empty($reviewRequest)) {
            throw new LogicException('the plan confirm already exists', ErrorCode::THE_PLAN_CONFIRM_ALREADY_EXISTS);
        }

        $taskRelationTagModel = model(TaskRelationTagModel::class);
        try {
            if (!empty($taskIds)) {
                $taskRelationTagModel->startTrans();
                $taskRelationTagList = $taskRelationTagModel->where(['task_id' => ["IN", $taskIds]])->select();
                if (!empty($taskRelationTagList)) {
                    $taskRelationTagIds = [];
                    foreach ($taskRelationTagList as $taskRelationTagItem) {
                        $taskRelationTagIds[] = $taskRelationTagItem['id'];
                    }
                    //删除相关标签
                    $result['task_relation_tag'] = model(TaskRelationTagModel::class)->where(['id' => ['IN', $taskRelationTagIds]])->delete();
                }
                //删除代办人员
                $result['task_update'] = model(TaskMemberModel::class)->where(['task_id' => ["IN", $taskIds]])->delete();
                //删除代办
                $result['task'] = model(TaskModel::class)->where(['id' => ['IN', $taskIds]])->delete();
            }
            //删除计划
            if (!empty($planIds)) {
                $result['plan_update'] = model(PlanModel::class)->where(['id' => ['IN', $planIds]])->delete();
            }
            $taskRelationTagModel->commit();
            return $result;
        } catch (\Throwable $e) {
            $taskRelationTagModel->rollback();
            throw $e;
        }
    }

    /**
     * 计划同步影响计划
     * @param $plan
     * @param $userId
     * @param $tenantId
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function planSyncEffectPlan($plan, $userId, $tenantId)
    {
        /**
         * 1.判定类型 全局的还是单集的
         * 2.如若是全局 , 找出单集需要新增的 以及 覆盖的 ,限制条件: 单环节某集只能存在唯一的环节制作排期,因此单集新增之后不会再触发回全局的更新与新增
         *   a. 单集新增的 , 需要判定新增是否有环节工序冲突 (todo:产品待定)
         *   b. 单集覆盖的
         * 3.如若是单集, 找出全局需要新增的 以及覆盖的
         *   a. 全局新增 , 新增关联单集的全局排期
         *   b. 全局覆盖 , 先覆盖该全局的, 然后全局再覆盖其他单集 -> 回到 2 执行
         * 4. 覆盖内容 - 开始时间 ,结束时间, 执行人 , 参与人
         */

        // 接受过来的数据处理
        // 只有环节制作的排期才同步影响
        if ($plan['type'] != PlanModel::TYPE_STEP_CATEGORY) {
            return [];
        }

        //计划处理
        if ($plan['type'] != PlanModel::TYPE_STEP_CATEGORY) {
            return [];
        }

        $planScope = $plan['scope'];

        // 如果是全局的将影响单集的数据
        $affectedPlanIds = [];
        if ($planScope == PlanModel::SCOPE_GLOBAL) {
            $affectedPlanIds = $this->globalPlanSyncEffectPlan($plan, $userId, $tenantId);
        } else {
//            $affectedPlanIds = $this->singlePlanSyncEffectPlan($plan, $userId, $tenantId);
        }
        return $affectedPlanIds;
    }

    /**
     * 全局排期同步影响排期
     * @param $plan
     * @param $userId
     * @param $tenantId
     * @param null $skipEpisodeIds
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function globalPlanSyncEffectPlan($plan, $userId, $tenantId, $skipEpisodeIds = null)
    {
        $affectedPlanIds = [];
        //查找关联中是否有关联的集
        $planTaskRelationList = model(PlanTaskRelationModel::class)->where(['plan_id' => $plan['id']])->select();
        $taskIdList = array_column($planTaskRelationList, 'task_id');
        if (empty($taskIdList)) {
            return [];
        }
        $relationTagList = model(TaskRelationTagModel::class)->where(['task_id' => ['IN', $taskIdList]])->field("link_module_code,link_id,project_id")->group('link_module_code,link_id')->select();

        if (empty($relationTagList)) {
            return [];
        }

        $episodeIds = [];
        $stepCategoryTagList = [];
        $stepCategoryIds = [];
        foreach ($relationTagList as $relateTagItem) {
            if (in_array($relateTagItem['link_module_code'], ['step_category', 'show_step_category'])) {
                $stepCategoryIds[] = $relateTagItem['link_id'];
            }

            if ($relateTagItem['link_module_code'] == 'episode') {
                $episodeIds[] = $relateTagItem['link_id'];
                continue;
            }

            unset($relateTagItem['id'], $relateTagItem['task_id']);
            $stepCategoryTagList[] = $relateTagItem;
        }

        //跳过某些剧集,不用更新
        if (!empty($skipEpisodeIds)) {
            $episodeIds = array_diff($episodeIds, $skipEpisodeIds);
        }

        //找不到集的不更新
        if (empty($episodeIds) || empty($stepCategoryIds)) {
            return [];
        }
        // 获取任务详细内容
        $task = model(TaskModel::class)->field("id,description,tenant_id,project_id,plan_start_time,plan_end_time,step_type")->where(['id' => ['IN', $taskIdList]])->find();

        $task['plan_start_time'] = date('Y-m-d H:i:s', $plan['start_time']);
        $task['plan_end_time'] = date('Y-m-d H:i:s', $plan['end_time']);

        $taskId = $task['id'];
        unset($task['id']);
        // 获取任务成员
        $taskMemberList = model(TaskMemberModel::class)->where(['task_id' => $taskId])->select();

        $partner = [];
        $executor = [];
        if (!empty($taskMemberList)) {
            $userIdsData = [];
            foreach ($taskMemberList as $taskMemberItem) {
                if (!empty($taskMemberItem['partner'])) {
                    $userIdsData[$taskMemberItem['partner']] = $taskMemberItem['partner'];
                }
                if (!empty($taskMemberItem['user_id'])) {
                    $userIdsData[$taskMemberItem['user_id']] = $taskMemberItem['user_id'];
                }
            }
            $userIdsData = array_values($userIdsData);
            $userDataList = model(UserModel::class)->where(['id' => ["IN", $userIdsData]])->field("id,union_id")->select();
            $userDataList = array_column($userDataList, 'union_id', 'id');

            foreach ($taskMemberList as $taskMemberItem) {
                if (!empty($taskMemberItem['partner'])) {
                    $partner[] = $userDataList[$taskMemberItem['partner']];
                }
                if (!empty($taskMemberItem['user_id'])) {
                    $executor[] = $userDataList[$taskMemberItem['user_id']];
                }
            }
        }

        // 查找环节制作单集排期
        $singlePlanList = model(PlanModel::class)
            ->join("task_relation_tag on task_relation_tag.task_id = plan.task_id and task_relation_tag.link_module_code in ('step_category','show_step_category')")
            ->where(['plan.type' => PlanModel::TYPE_STEP_CATEGORY, 'plan.scope' => PlanModel::SCOPE_SINGLE, 'plan.entity_id' => ["IN", $episodeIds], 'task_relation_tag.link_id' => ["IN", $stepCategoryIds]])
            ->field("plan.id,plan.entity_id,plan.task_id")
            ->group("plan.id")
            ->select();

        //存在的集则进行覆盖 , 不存在的集则新增
        $singlePlanEpisodeIds = array_column($singlePlanList, "entity_id");
        //存在的
        $existEpisodeIds = array_intersect($episodeIds, $singlePlanEpisodeIds);
        //不存在
        $noExistEpisodeIds = array_diff($episodeIds, $singlePlanEpisodeIds);

        //新增
        if (!empty($noExistEpisodeIds)) {
            // 创建数据
            $planDataList = [];
            $planDataTaskIds = [];

            $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();

            //默认名字处理
            $episodeNameMap = $this->dealEpisodePlanDefaultName($noExistEpisodeIds, $stepCategoryIds[0]);
            foreach ($noExistEpisodeIds as $noExistEpisodeId) {
                $currentPlan = $plan;
                $currentTask = $task;
                $currentTag = $stepCategoryTagList;
                $currentTag[] = [
                    'link_module_code' => 'episode',
                    'link_id' => $noExistEpisodeId
                ];
                $currentPlan['scope'] = PlanModel::SCOPE_SINGLE;
                $currentPlan['entity_id'] = $noExistEpisodeId;
                $currentPlan['uuid'] = create_uuid();
                $currentTask['episode_id'] = $noExistEpisodeId;

                // 默认名字处理
                $currentTask['name'] = $episodeNameMap[$noExistEpisodeId] ?? '';
                unset($currentPlan['id'], $currentPlan['task_id'], $currentTask['id']);
                // 取消标准制作待办
                if ($task['step_type'] == TaskModel::STEP_TYPE_STANDARD_MAKE_TO_DO) {
                    $currentTask['task_status'] = $closedStatus['id'];
                }
                $taskData = [
                    "task" => $currentTask,
                    "executor" => $executor,
                    "partner" => $partner,
                    "relate_tag" => $currentTag
                ];

                //验证比较逻辑 todo
                //新增逻辑
                $result = TaskMemberService::getInstance()->create($taskData, $userId, $tenantId);
                $currentPlan['task_id'] = $result['task']['id'];
                $planDataTaskIds[] = $result['task']['id'];
                $planDataList[] = $currentPlan;
            }

            //批量添加排期
            if (!empty($planDataList)) {
                model(PlanModel::class)->addAll($planDataList);
                //查询计划id
                $affectedPlanList = model(PlanModel::class)->where(['task_id' => ["IN", $planDataTaskIds]])->select();
                $affectedPlanIds = array_column($affectedPlanList, 'id');
                $addPlanTaskRelationList = [];
                foreach ($affectedPlanList as $affectedPlan) {
                    $addPlanTaskRelationList[] = [
                        'plan_id' => $affectedPlan['id'],
                        'task_id' => $affectedPlan['task_id'],
                        'created' => time(),
                        'created_by' => fill_created_by(),
                    ];
                }

            }
        }
        // add plan task relation data
        if (!empty($addPlanTaskRelationList)) {
            model(PlanTaskRelationModel::class)->addAll($addPlanTaskRelationList);
        }

        //覆盖
        if (!empty($existEpisodeIds)) {
            $existSinglePlanList = [];
            $existSingleTaskIds = [];
            //获取单集的计划 , 任务id
            foreach ($singlePlanList as $singlePlanItem) {
                if (in_array($singlePlanItem['entity_id'], $existEpisodeIds)) {
                    $existSinglePlanList[] = $singlePlanItem;
                    $existSingleTaskIds[] = $singlePlanItem['task_id'];
                }
            }

            //获取
            $existSingleTaskList = model(TaskModel::class)->where(['id' => ["IN", $existSingleTaskIds]])
                ->field("id,name,description,project_id,plan_start_time,plan_end_time,step_type")
                ->select();

            // 将时间覆盖
            foreach ($existSinglePlanList as $existSinglePlanIndex => $existSinglePlanItem) {
                $existSinglePlanItem['start_time'] = $plan['start_time'];
                $existSinglePlanItem['end_time'] = $plan['end_time'];
                $affectedPlanIds[] = $existSinglePlanItem['id'];
                $existSinglePlanList[$existSinglePlanIndex] = $existSinglePlanItem;
            }
            //任务覆盖 - 内容 , 执行人 , 参与人
            $existSingleTaskData = [];
            foreach ($existSingleTaskList as $existSingleTaskItem) {
                $existSingleTaskItem['plan_start_time'] = date('Y-m-d H:i:s', $existSingleTaskItem['plan_start_time']);
                $existSingleTaskItem['plan_end_time'] = date('Y-m-d H:i:s', $existSingleTaskItem['plan_end_time']);
                $existSingleTaskItem = array_merge($existSingleTaskItem, $task);
                $existSingleTaskData[] = [
                    'task' => $existSingleTaskItem,
                    'executor' => $executor,
                    'partner' => $partner,
                ];
            }

            $planModel = model(PlanModel::class);

            try {
                $planModel->startTrans();
                foreach ($existSingleTaskData as $existSingleTaskDataItem) {
                    // todo : 优化成批量更改
                    TaskMemberService::getInstance()->updateTask($existSingleTaskDataItem, $userId);
                }
                $planModel->saveAll($existSinglePlanList);
                $planModel->commit();
            } catch (Throwable $e) {
                $planModel->rollback();
                throw $e;
            }
        }

        //返回收到影响的计划id
        return $affectedPlanIds;
    }

    /**
     * 单集排期同步更新
     * @param $plan
     * @param $userId
     * @param $tenantId
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function singlePlanSyncEffectPlan($plan, $userId, $tenantId)
    {
        $affectedPlanIds = [];

        // 查找到 与该集相关的全局计划
        $episodeId = $plan['entity_id'];


        $plan['start_time'] = date('Y-m-d H:i:s', $plan['start_time']);
        $plan['end_time'] = date('Y-m-d H:i:s', $plan['end_time']);

        //拿取到工序分类id
        $planTagList = model(TaskRelationTagModel::class)->where(['task_id' => $plan['task_id']])->field("link_module_code,link_id")->select();

        $stepCategoryTagIds = [];
        foreach ($planTagList as $planTagItem) {
            if (in_array($planTagItem['link_module_code'], ['show_step_category', 'step_category'])) {
                $stepCategoryTagIds[] = $planTagItem['link_id'];
            }
        }

        if (empty($stepCategoryTagIds)) {
            return [];
        }

        $planModel = model(PlanModel::class);
        $globalPlan = $planModel
            ->join("task_relation_tag r_t_episode on r_t_episode.task_id = plan.task_id and r_t_episode.link_module_code = 'episode'")
            ->join("task_relation_tag r_t_step_category on r_t_step_category.task_id = plan.task_id and r_t_step_category.link_module_code in ('step_category','show_step_category') ")
            ->where([
                'plan.project_id' => $plan['project_id'],
                'plan.scope' => PlanModel::SCOPE_GLOBAL,
                'r_t_episode.link_id' => $episodeId,
                'r_t_step_category.link_id' => ["IN", $stepCategoryTagIds]
            ])->field("plan.id,plan.tenant_id,plan.project_id,plan.start_time,plan.end_time,plan.task_id")->find();

        $taskMemberList = model(TaskMemberModel::class)->where(['task_id' => $plan['task_id']])->select();
        $partner = [];
        $executor = [];
        if (!empty($taskMemberList)) {
            foreach ($taskMemberList as $taskMemberItem) {
                if (!empty($taskMemberItem['partner'])) {
                    $partner[] = $taskMemberItem['partner'];
                }
                if (!empty($taskMemberItem['user_id'])) {
                    $executor[] = $taskMemberItem['user_id'];
                }
            }
        }
        $globalTask = model(TaskModel::class)->field("name,description,plan_start_time,plan_end_time,step_type,project_id")->find($plan['task_id']);
        $globalTask['plan_start_time'] = date('Y-m-d H:i:s', $globalTask['plan_start_time']);
        $globalTask['plan_end_time'] = date('Y-m-d H:i:s', $globalTask['plan_end_time']);

        //判定是否保存还是新增
        if (!empty($globalPlan)) {
            $globalPlan['start_time'] = $plan['start_time'];
            $globalPlan['end_time'] = $plan['end_time'];
            $globalTask['id'] = $globalPlan['task_id'];

            // 名字不覆盖
            unset($globalTask['name']);

            $taskData = [
                'task' => $globalTask,
                'partner' => $partner,
                'executor' => $executor,
            ];
            $planModel->startTrans();
            try {
                //更新全局排期
                $modifyPlanData = model(PlanModel::class)->modifyItem($globalPlan);
                if (!empty($modifyPlanData)) {
                    $affectedPlanIds[] = $modifyPlanData['id'];
                }
                //计划时间同步
                TaskMemberService::getInstance()->updateTask($taskData, $userId);
                // 通过全局在更新其他计划
                $globalPlan['start_time'] = strtotime($globalPlan['start_time']);
                $globalPlan['end_time'] = strtotime($globalPlan['end_time']);
                $affectedGlobalPlanIds = $this->globalPlanSyncEffectPlan($globalPlan, $userId, $tenantId, [$episodeId]);
                $affectedPlanIds = array_merge($affectedPlanIds, $affectedGlobalPlanIds);
                $planModel->commit();
            } catch (Throwable $e) {
                $planModel->rollback();
                throw $e;
            }
        } else {
            //需要复制的字段
            $globalFields = ['start_time', 'end_time', 'tenant_id', 'type', 'project_id'];
            $globalPlan = [
                'scope' => PlanModel::SCOPE_GLOBAL
            ];
            foreach ($plan as $planKey => $planValue) {
                if (in_array($planKey, $globalFields)) {
                    $globalPlan[$planKey] = $planValue;
                }
            }

            //同步项目id
            $taskData = [
                "task" => $globalTask,
                "executor" => $executor,
                "partner" => $partner,
                "relate_tag" => $planTagList
            ];

            $planModel->startTrans();
            try {
                $result = TaskMemberService::getInstance()->create($taskData, $userId, $tenantId);
                $globalPlan['task_id'] = $result['task']['id'];
                $result['plan'] = model(PlanModel::class)->addItem($globalPlan);

                $affectedPlanIds[] = $result['plan']['id'];
                $planModel->commit();
            } catch (Throwable $e) {
                $planModel->rollback();
                throw $e;
            }
        }

        return $affectedPlanIds;
    }

    /**
     * 处理创建工序分类计划数据
     * @param $data
     * @param $planData
     * @param $isValidatePlan
     * @param $deletePlan
     * @return array|mixed
     * @throws \think\Exception
     */
    public function dealCreateStepCategoryPlanData(&$data, &$planData, $isValidatePlan, &$deletePlan)
    {
        $producerStepCategoryList = ProjectService::getInstance()->getShowStepCategoryList($planData['project_id']);
        $this->validatePlan($data, $producerStepCategoryList, $isValidatePlan);

        $plan = [];

        $relateTagLinkIds = [];
        if ($planData['scope'] == PlanModel::SCOPE_SINGLE && !empty($data["relate_tag"])) {
            foreach ($data["relate_tag"] as $relateTagItem) {
                if ($relateTagItem['link_module_code'] == 'episode') {
                    //单集补充实体id
                    $planData['entity_id'] = $relateTagItem['link_id'];
                }

                //获取环节标签id
                if (in_array($relateTagItem['link_module_code'], ['show_step_category', 'step_category'])) {
                    $relateTagLinkIds[] = $relateTagItem['link_id'];
                }
            }
        }

        //环节制作的需要验证
        if ($planData['type'] == PlanModel::TYPE_STEP_CATEGORY) {

            // 单集 需进行 覆盖 判定
            if ($planData['scope'] == PlanModel::SCOPE_SINGLE) {
                //获取指定集下的工序分类计划
                $planList = model(PlanModel::class)
                    ->where([
                        'project_id' => $planData['project_id'],
                        'type' => PlanModel::TYPE_STEP_CATEGORY,
                        'entity_id' => $planData['entity_id'],
                        'attribute' => $planData['attribute'] ?? '',
                        'scope' => PlanModel::SCOPE_SINGLE,
                        ])
                    ->select();

                //如果为空直接跳过 验证
                if (!empty($planList)) {
                    $planTaskIds = [];
                    $planTaskMap = [];
                    foreach ($planList as $planItem) {
                        $planTaskIds[] = $planItem['task_id'];
                        $planTaskMap[$planItem['task_id']] = $planItem;
                    }

                    //比较工序分类是否相同
                    $exactlySameTaskId = $this->detectionCompleteCoverage($planTaskIds, $relateTagLinkIds);

                    if (!empty($exactlySameTaskId)) {
                        // 拿取到需要覆盖的计划
                        $plan = $planTaskMap[$exactlySameTaskId];
                        //内容覆盖
                        $planData = array_merge($plan, $planData);
                        $task = model(TaskModel::class)->field("id,name,description,plan_end_time,plan_start_time,step_type")->find($plan['task_id']);
                        // 待办覆盖
                        $data["task"] = array_merge($task, $data["task"]);
                    }
                }

            } elseif ($planData['scope'] == PlanModel::SCOPE_GLOBAL) {
                // 单环节某集只能存在唯一的环节制作排期 ,有相同集的需要覆盖

                $stepCategoryIds = [];
                $episodeTagIds = [];
                foreach ($data["relate_tag"] as $relateTagItem) {
                    if ($relateTagItem['link_module_code'] == 'episode') {
                        $episodeTagIds[] = $relateTagItem['link_id'];
                    }

                    //获取环节标签id
                    if (in_array($relateTagItem['link_module_code'], ['show_step_category', 'step_category'])) {
                        $stepCategoryIds[] = $relateTagItem['link_id'];
                    }
                }
                // 相同的需要覆盖 , 全局的 , 取出第一个 进行 覆盖,如果还有其他的则需要删除
                if (!empty($episodeTagIds)) {
                    $planList = model(PlanModel::class)
                        ->join("task_relation_tag e_tag on e_tag.task_id = plan.task_id and e_tag.link_module_code = 'episode'")
                        ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
                        ->where([
                            'plan.project_id' => $planData['project_id'],
                            'plan.attribute' => $planData['attribute'] ?? '',
                            'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
                            'plan.scope' => PlanModel::SCOPE_GLOBAL,
                            'e_tag.link_id' => ["IN", $episodeTagIds],
                            's_tag.link_id' => ["IN", $stepCategoryIds],
                        ])
                        ->field("plan.*")
                        ->group("plan.id")
                        ->select();

                    //需要覆盖的
                    if (!empty($planList)) {
                        $plan = array_shift($planList);
                        //需要删除的
                        $deletePlan = $planList;
                        $planData['id'] = $plan['id'];
                    }
                }


            }
        }

        return $plan;
    }

    /**
     * 查询环节交付时间
     * @param $episodeStepCategoryWhere
     * @return array
     */
    public function getStepCategoryPlanData($episodeStepCategoryWhere)
    {
        $planTimeMap = [];
        if (empty($episodeStepCategoryWhere)) {
            return $planTimeMap;
        }

        $planList = model(PlanModel::class)
            ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
            ->where([
                "plan.scope" => PlanModel::SCOPE_SINGLE,
                "plan.type" => PlanModel::TYPE_STEP_CATEGORY,
                $episodeStepCategoryWhere
            ])
            ->field("plan.*,s_tag.link_id")
            ->select();

        foreach ($planList as $planItem) {
            $planTimeMap[$planItem['entity_id'] . '#' . $planItem['link_id']] = $planItem;
        }
        return $planTimeMap;
    }

    /**
     * 查询环节交付时间
     * @param $episodeId
     * @param $stepCategoryId
     * @return int|null
     */
    public function getStepCategoryPlanEndTimeOne($episodeId, $stepCategoryId)
    {
        if (empty($episodeId) || empty($stepCategoryId)) {
            return null;
        }
        $episode = model(EntityModel::class)
            ->join("project on project.id = entity.project_id")
            ->join("entity source_entity on source_entity.module_id = entity.module_id and source_entity.project_id = project.belong_project_id and entity.code = source_entity.code", 'LEFT')
            ->where(['entity.id' => $episodeId])
            ->field('entity.id,source_entity.id source_id')
            ->find();
        if (!empty($episode['source_id'])) {
            $episodeId = $episode['source_id'];
        } else {
            $episodeId = $episode['id'];
        }
        $plan = model(PlanModel::class)
            ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
            ->where([
                'plan.entity_id' => $episodeId,
                "plan.scope" => PlanModel::SCOPE_SINGLE,
                "plan.type" => PlanModel::TYPE_STEP_CATEGORY,
                's_tag.link_id' => $stepCategoryId
            ])
            ->field("plan.entity_id as episode_id,plan.end_time,s_tag.link_id")
            ->find();

        if (empty($plan)) {
            return null;
        }
        return $plan['end_time'];
    }

    /**
     * 查询环节交付时间
     * @param $episodeIds
     * @param $stepCategoryIds
     * @return array
     */
    public function getStepCategoryPlanEndTime($episodeIds, $stepCategoryIds, $mapPlanId = false)
    {
        $planTimeMap = [];
        if (empty($episodeIds) || empty($stepCategoryIds)) {
            return $planTimeMap;
        }
        $episodeList = model(EntityModel::class)
            ->join("project on project.id = entity.project_id")
            ->join("entity source_entity on source_entity.module_id = entity.module_id and source_entity.project_id = project.belong_project_id and entity.code = source_entity.code", 'LEFT')
            ->where(['entity.id' => ['IN', implode(',', $episodeIds)]])
            ->field('entity.id,source_entity.id source_id')
            ->select();
        $episodeMap = array_column($episodeList, 'id', 'source_id');
        $episodeIds = [];
        foreach ($episodeList as $episode) {
            if (!empty($episode['source_id'])) {
                $episodeIds[] = $episode['source_id'];
            } else {
                $episodeIds[] = $episode['id'];
            }
        }
        $planList = model(PlanModel::class)
            ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
            ->where([
                'plan.entity_id' => ["IN", implode(',', $episodeIds)],
                "plan.scope" => PlanModel::SCOPE_SINGLE,
                "plan.type" => PlanModel::TYPE_STEP_CATEGORY,
                's_tag.link_id' => ["IN", implode(',', $stepCategoryIds)]
            ])
            ->field("plan.id as plan_id,plan.entity_id as episode_id,plan.end_time,s_tag.link_id")
            ->select();

        foreach ($planList as $planItem) {
            $episodeId = $planItem['episode_id'];
            if (!empty($episodeMap[$planItem['episode_id']])) {
                $episodeId = $episodeMap[$planItem['episode_id']];
            }
            if ($mapPlanId) {
                $planTimeMap[$episodeId][$planItem['link_id']] = [
                    "id" => $planItem['plan_id'],
                    "end_time" => $planItem['end_time']
                ];
            } else {
                $planTimeMap[$episodeId][$planItem['link_id']] = $planItem['end_time'];
            }
        }
        return $planTimeMap;
    }

    /**
     * 查询环节交付对象
     * @param $episodeIds
     * @param $stepCategoryIds
     * @return array
     */
    public function getEpisodeStepCategoryPlanObject($episodeIds, $stepCategoryIds)
    {
        $planTimeMap = [];
        if (empty($episodeIds) || empty($stepCategoryIds)) {
            return $planTimeMap;
        }

        $episodeList = model(EntityModel::class)
            ->join("project on project.id = entity.project_id")
            ->join("entity source_entity on source_entity.module_id = entity.module_id and source_entity.project_id = project.belong_project_id and entity.code = source_entity.code", 'LEFT')
            ->where(['entity.id' => ['IN', implode(',', $episodeIds)]])
            ->field('entity.id,source_entity.id source_id')
            ->select();
        $episodeMap = array_column($episodeList, 'id', 'source_id');
        $episodeIds = [];
        foreach ($episodeList as $episode) {
            if (!empty($episode['source_id'])) {
                $episodeIds[] = $episode['source_id'];
            } else {
                $episodeIds[] = $episode['id'];
            }
        }

        $planList = model(PlanModel::class)
            ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
            ->where([
                'plan.entity_id' => ["IN", implode(',', $episodeIds)],
                "plan.scope" => PlanModel::SCOPE_SINGLE,
                "plan.type" => PlanModel::TYPE_STEP_CATEGORY,
                's_tag.link_id' => ["IN", implode(',', $stepCategoryIds)]
            ])
            ->field("plan.*,plan.entity_id as episode_id,plan.end_time,s_tag.link_id")
            ->select();

        foreach ($planList as $planItem) {
            $episodeId = $planItem['episode_id'];
            if (!empty($episodeMap[$planItem['episode_id']])) {
                $episodeId = $episodeMap[$planItem['episode_id']];
            }
            //判断属性排期
            if ($planItem['attribute']) {
                $planTimeMap[$episodeId][$planItem['link_id'].'#'.$planItem['attribute']] = $planItem;
            } else {
                $planTimeMap[$episodeId][$planItem['link_id']] = $planItem;
            }
        }
        return $planTimeMap;
    }

    /**
     * 创建排期确认
     * @param $data
     * @return array
     * @throws \Throwable
     * @throws \think\Exception
     */
    public function createPlanConfirmation($data)
    {
        // 确认人
        $confirmationUser = $data['user_id'];
        $planId = $data['plan_id'];
        //确认截至时间
        $confirmationEndTime = $data['confirm_end_time'];

        $plan = model(PlanModel::class)->field("id,project_id,task_id")->find($planId);

        if (empty($plan)) {
            throw new LogicException('plan not found', ErrorCode::PLAN_NOT_FOUND);
        }

        $task = model(TaskModel::class)->field('name')->find($plan['task_id']);

        $reviewRequest = model(ReviewRequestModel::class)
            ->where(['module_id' => module_by_code('plan')['id'], 'link_id' => $plan['id'], 'project_id' => $plan['project_id']])
            ->find();

        if (!empty($reviewRequest)) {
            throw new LogicException('the plan already confirm', ErrorCode::THE_PLAN_ALREADY_CONFIRM);
        }

        $reviewRequestData = [
            'data' => [
                'link_id' => $planId,
                'module_id' => module_by_code("plan")['id'],
                'name' => $task['name'],
                'review_workflow_id' => 0,
                'description' => $data['description'] ?? "",
                'project_id' => $plan['project_id'],
            ],
            "reviewers" => [[
                'role_code' => 'default',
                'user_id' => $confirmationUser,
                'skip' => 0,
                'confirm_end_time' => $confirmationEndTime
            ]]

        ];
        //创建审核
        return ReviewRequestService::getInstance()->create($reviewRequestData);
    }

    /**
     * 获取排期确认列表
     * @param $param
     * @return array
     */
    public function getPlanConfirmList($param)
    {
        $filter = $param['filter'];
        $page = $param['page'] ?? [1, C('default_page_total')];

        $reviewRequestList = model(ReviewRequestModel::class)
            ->join('review_request_node on review_request_node.review_request_id = review_request.id')
            ->where($filter)
            ->where([
                'module_id' => module_by_code('plan')['id']
            ])
            ->page(...$page)
            ->field('review_request.id,review_request.link_id,review_request.name,review_request.is_reviewed,review_request.is_pass,review_request.description,review_request_node.id as review_request_node_id,review_request_node.confirm_end_time,review_request_node.plan_reviewer')
            ->select();

        if (empty($reviewRequestList)) {
            return [];
        }
        $reviewRequestUserIds = array_unique(array_column($reviewRequestList, 'plan_reviewer'));
        $userList = model(UserModel::class)->where(['id' => ['IN', $reviewRequestUserIds]])->field("id,name,avatar")->select();
        $userList = array_column($userList, null, 'id');
        return array_map(function ($reviewRequestItem) use ($userList) {
            $reviewRequestItem['user'] = $userList[$reviewRequestItem['plan_reviewer']] ?? [];
            return $reviewRequestItem;
        }, $reviewRequestList);
    }

    /**
     * 批量更新排期确认
     * @param $data
     * @return array
     * @throws \think\Exception
     * @throws \Exception
     * @throws \Throwable
     */
    public function batchUpdatePlanConfirm($data)
    {
        $planIds = $data['plan_ids'];
        $projectIds = $data['project_id'];

        $reviewRequestModel = model(ReviewRequestModel::class);
        $reviewRequestList = $reviewRequestModel
            ->join("review_request_node on review_request_node.review_request_id = review_request.id")
            ->where([
                'review_request.project_id' => $projectIds,
                'review_request.module_id' => module_by_code('plan')['id'],
                'review_request.link_id' => ["IN", $planIds],
            ])
            ->field('review_request_node.id as review_request_node_id,review_request.id,review_request.is_pass')
            ->select();

        // 批量确认
        $noReviewRequestIds = [];
        $noReviewRequestNodeIds = [];
        if ((isset($data['is_pass']) && $data['is_pass'] == 'yes') || isset($data['confirm_end_time']) || isset($data['plan_reviewer'])) {
            foreach ($reviewRequestList as $reviewRequestItem) {
                if ($reviewRequestItem['is_pass'] == 'no') {
                    $noReviewRequestIds[] = $reviewRequestItem['id'];
                    $noReviewRequestNodeIds[] = $reviewRequestItem['review_request_node_id'];
                }
            }

            if (empty($noReviewRequestNodeIds)) {
                return [];
            }


            if (isset($data['is_pass']) && $data['is_pass'] == 'yes') {
                $reviewRequestModel->startTrans();
                try {
                    model(ReviewRequestNodeModel::class)
                        ->where(['id' => ["IN", $noReviewRequestNodeIds]])
                        ->save(['is_reviewed' => 'yes', 'is_pass' => "yes", 'is_active' => 'no', 'reviewed' => time()]);

                    $reviewRequestModel
                        ->where(['id' => ["IN", $noReviewRequestIds]])
                        ->save(['is_pass' => "yes", 'is_reviewed' => "yes", 'reviewed' => time(),]);

                    model(PlanModel::class)->where(['id' => ["IN", $planIds]])->save(['confirm_status' => 'yes']);
                    $reviewRequestModel->commit();
                } catch (\Throwable $e) {
                    $reviewRequestModel->rollback();
                    throw $e;
                }
            }

            unset($data['is_pass'], $data['plan_ids'], $data['project_id']);

            if (isset($data['confirm_end_time']) || isset($data['plan_reviewer'])) {
                model(ReviewRequestNodeModel::class)->where(['id' => ["IN", $noReviewRequestNodeIds]])->save($data);
            }

            return $noReviewRequestIds;
        }
        return $noReviewRequestIds;
    }

    /**
     * 处理剧集名字
     * @param $episodeIds
     * @param $stepCategoryId
     * @return array
     * @throws \think\Exception
     */
    public function dealEpisodePlanDefaultName($episodeIds, $stepCategoryId)
    {
        $episodeList = model(EntityModel::class)->where(['id' => ["IN", $episodeIds]])->field("id,project_id,name")->select();

        if (empty($episodeList)) {
            return [];
        }

        $projectId = $episodeList[0]['project_id'];

        //拿到show_name
        $stepCategoryList = ProjectService::getInstance()->getShowStepCategoryList($projectId);

        $showName = "";
        foreach ($stepCategoryList as $stepCategoryItem) {
            if ($stepCategoryItem['id'] == $stepCategoryId) {
                $showName = $stepCategoryItem['show_name'];
            }
        }

        if (empty($showName)) {
            return [];
        }

        $episodeNameMap = [];
        foreach ($episodeList as $episodeItem) {
            $episodeName = $episodeItem['name'];
            $episodeNameMap[$episodeItem['id']] = "{$episodeName}{$showName}制作任务";
        }

        return $episodeNameMap;

    }

    /**
     * 添加计划版本
     * @param $source
     * @param $planData
     * @param $oldPlan
     * @throws \Exception
     */
    public function addPlanVersion($source, $planData, $oldPlan)
    {
        $planId = $planData['id'];

        //判定是否有数据的更改 plan
        if (!empty($oldPlan)) {
            $isUpdate = false;
            foreach ($planData as $planDataKey => $planDataItem) {
                if (!$isUpdate && $oldPlan[$planDataKey] != $planDataItem) {
                    $isUpdate = true;
                }
            }
            if (!$isUpdate) {
                return;
            }
        }

        model(RecordModel::class)->addItem([
            'source' => $source,
            'link_id' => $planId,
            'module_id' => module_by_code("plan")["id"],
            "json" => ["old" => $oldPlan, "new" => $planData]
        ]);
    }


    /**
     * 查询环节交付对象
     * @param $episodeIds
     * @param $stepCategoryIds
     * @param $fields
     * @return array|mixed|string
     */
    public function getEpisodeStepCategoryPlanList($episodeIds, $stepCategoryIds, $fields)
    {
        if (empty($episodeIds) || empty($stepCategoryIds)) {
            return [];
        }

        $episodeList = model(EntityModel::class)
            ->join("project on project.id = entity.project_id")
            ->join("entity source_entity on source_entity.module_id = entity.module_id and source_entity.project_id = project.belong_project_id and entity.code = source_entity.code", 'LEFT')
            ->where(['entity.id' => ['IN', implode(',', $episodeIds)]])
            ->field('entity.id,source_entity.id source_id')
            ->select();
        if (empty($episodeList)) {
            return [];
        }

        $episodeIds = [];
        foreach ($episodeList as $episode) {
            if (!empty($episode['source_id'])) {
                $episodeIds[] = $episode['source_id'];
            } else {
                $episodeIds[] = $episode['id'];
            }
        }

        $planList = model(PlanModel::class)
            ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
            ->where([
                'plan.entity_id' => ["IN", implode(',', $episodeIds)],
                "plan.scope" => PlanModel::SCOPE_SINGLE,
                "plan.type" => PlanModel::TYPE_STEP_CATEGORY,
                's_tag.link_id' => ["IN", implode(',', $stepCategoryIds)]
            ])
            ->field($fields)
            ->select();

        if (empty($planList)) {
            return [];
        }
        return $planList;
    }

    /**
     * 批量刷排期时间到任务
     * @param $data
     * @return void
     */
    public function batchUpdatePlanTimeTask($data)
    {

    }

    /**
     * 获取原计划排期节点列表
     * @param $param
     * @return array
     */
    public function getOriginalPlanNode($param)
    {
        $where = [];
        //查询条件
        $where['opn.project_id'] = $param['project_id'];
        if (isset($param['step_category_id'])) {
            $stepCategoryId = explode(',', $param['step_category_id']);
            if ($stepCategoryId) {
                $where['opn.step_category_id'] = ['in', $stepCategoryId];
            }
        }
        //查询时间
        if ($param['start_time'] && $param['end_time']) {
            $where['opn.delivery_time'] = ['BETWEEN', [$param['start_time'], $param['end_time']]];
        }

        //查询列表
        $list = model(OriginalPlanNodeModel::class)->alias('opn')
            ->join("entity on entity.id = opn.episode_id")
            ->where($where)
            ->field('entity.name as episode_name,opn.id,opn.delivery_time,opn.type,opn.all_task_ids,opn.delivery_task_ids,opn.step_category_id')
            ->limit(C("database.database_max_select_rows"))
            ->select();

        return $list;
    }


    /**
     * 更新排期节点---锁定操作
     * @param $data
     * @return array|bool
     * @throws \Exception
     */
    public function addPlanLock($data)
    {
        //查询排期
        $planInfo = model(PlanModel::class)->find($data['plan_id']);
        if (empty($planInfo)) {
            throw new LogicException('排期未找到', ErrorCode::PLAN_NOT_FOUND);
        }
        if ($planInfo['is_lock'] == PlanModel::IS_LOCK_YES) {
            throw new LogicException('排期已锁定', ErrorCode::PLAN_IS_LOCK_YES);
        }

        //判断节点是否存在
        $orgInfo = model(OriginalPlanNodeModel::class)->where(['link_id' => $data['plan_id'], 'type' => ['in', [OriginalPlanNodeModel::TYPE_INNER_PLAN, OriginalPlanNodeModel::TYPE_STANDARD_PLAN]]])->find();
        if (empty($orgInfo)) {
            throw new LogicException('未找到排期节点', ErrorCode::ORIGIN_PLAN_NOT_FOUND);
        }

        //标准排期 和 非标排期--任务逻辑不一样
        if (!in_array($planInfo['type'], [PlanModel::TYPE_REVIEW, PlanModel::TYPE_MEETING, PlanModel::TYPE_TO_DO])) {
            //判断非标排期---是否有任务
            $this->judgeUnStandardPlanTask($planInfo);
        }

        $modifyData = [
            'id' => $planInfo['id'],
            'is_lock' => PlanModel::IS_LOCK_YES,
        ];

        return model(PlanModel::class)->modifyItem($modifyData);
    }


    /**
     * 判断生产排期---是否有任务
     * @param $planInfo
     * @return void
     */
    private function judgeUnStandardPlanTask($planInfo)
    {
        //查询剧集Code
        $episodeCode = model(EntityModel::class)->where(['id' => $planInfo['entity_id']])->value('name');
        //查询--环节IDs
        $categoryIds = model(TaskRelationTagModel::class)
            ->where(['task_id' => $planInfo['task_id'], 'link_module_code' => ['in', ['show_step_category', 'step_category']]])
            ->column('link_id');
        if (empty($categoryIds)) {
            throw new LogicException('排期环节未找到', ErrorCode::DATA_INVALID);
        }

        //组内关联资产
        $relatedWhere = [];
        if ($planInfo['related_entity']) {
            $relatedEntity = explode(',', $planInfo['related_entity']);
            $relatedWhere['t.source_entity_id'] = ['in', $relatedEntity];
        }
        //判断 ---排期下 是否 有未发布的镜头或资产
        $hasNoEntity = model(EntityModel::class)
            ->where([
                'project_id' => $planInfo['project_id'],
                'initial_episode_code' => $episodeCode,
                'module_id' => ['in', [module_by_code('asset')['id'], module_by_code('shot')['id']]],
                'is_generate' => 'no'
            ])
            ->field('id')
            ->find();
        if ($hasNoEntity) {
            throw new LogicException('排期下有未发布的镜头或资产', ErrorCode::PLAN_ENTITY_NO_PUBLISH);
        }

        //判断 ---排期下  环节下任务
        $palnTaskNum = model(TaskModel::class)->alias('t')
            ->join('entity e on t.entity_id = e.id')
            ->field("SUM(if(t.is_assignee='no', 1,0)) as task_no_num, count(*) as task_num")
            ->where($relatedWhere)
            ->where([
                'e.initial_episode_code' => $episodeCode,
                't.step_category_id' => ['in', $categoryIds],
                't.project_id' => $planInfo['project_id']
            ])
            ->where(['t.step_type' => TaskModel::STEP_TYPE_STEP])
            ->find();

        if (empty($palnTaskNum['task_num'])) {
            throw new LogicException('排期下没有任务', ErrorCode::PLAN_NOT_ENTITY);
        }
    }

}


